gusucode.com > VC++ 图片浏览器的设计与实现+设计文档源码程序 > VC++ 图片浏览器的设计与实现+设计文档源码程序/code/PictView/Lib/dibapi.cpp

    //Download by http://www.NewXing.com
//********************************************************************** 
// 
//  DIBAPI.CPP
// 
//  Source file for Device-Independent Bitmap (DIB) API. 
// 
//********************************************************************** 
 
#define     STRICT      // enable strict type checking 
 
#include "stdafx.h"
#include <string.h> 
#include <stdio.h> 
#include <math.h> 
#include <io.h> 
#include <direct.h> 
#include <stdlib.h> 
#include <assert.h> 
#include <windows.h> 
#include <windowsx.h> 
#include <mmsystem.h> 
#include "dibapi.h" 

////////////////////////////////////////////////////////////////////////////////////
// Local use only function prototypes
////////////////////////////////////////////////////////////////////////////////////
BOOL CopyColorTable(LPBITMAPINFO lpTarget, LPBITMAPINFO lpSource, HPALETTE hPalSrc);
void DecreaseContrast(BYTE *pByte, const int Level, const float Grad);
void IncreaseContrast(BYTE *pByte, const int Low, const int High, const float Grad);
double Value(double n1, double n2, double hue);
HGLOBAL DitherImage(LPBYTE lpImageData, WORD Width, WORD Height);
void DitherLine(WORD Width, LPBYTE pLine1, LPBYTE pLine2);
////////////////////////////////////////////////////////////////////////////////////
 
/************************************************************************* 
 * 
 * CreateDIB() 
 * 
 * Parameters: 
 * 
 * DWORD dwWidth    - Width for new bitmap, in pixels 
 * DWORD dwHeight   - Height for new bitmap  
 * WORD  wBitCount  - Bit Count for new DIB (1, 4, 8, or 24) 
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function allocates memory for and initializes a new DIB by 
 * filling in the BITMAPINFOHEADER, allocating memory for the color 
 * table, and allocating memory for the bitmap bits.  As with all 
 * HDIBs, the header, colortable and bits are all in one contiguous 
 * memory block.  This function is similar to the CreateBitmap()  
 * Windows API. 
 * 
 * The colortable and bitmap bits are left uninitialized (zeroed) in the 
 * returned HDIB. 
 * 
 * 
 ************************************************************************/ 
HDIB CreateDIB(DWORD dwWidth, DWORD dwHeight, WORD wBitCount) 
{ 
    BITMAPINFOHEADER    bi;             // bitmap header 
    LPBITMAPINFOHEADER  lpbi;           // pointer to BITMAPINFOHEADER 
    DWORD               dwLen;          // size of memory block 
    HDIB                hDIB; 
    DWORD               dwBytesPerLine; // Number of bytes per scanline 
 
 
    // Make sure bits per pixel is valid 
 
    if (wBitCount <= 1) 
        wBitCount = 1; 
    else if (wBitCount <= 4) 
        wBitCount = 4; 
    else if (wBitCount <= 8) 
        wBitCount = 8; 
    else if (wBitCount <= 24) 
        wBitCount = 24; 
    else 
        wBitCount = 4;  // set default value to 4 if parameter is bogus 
 
    // initialize BITMAPINFOHEADER 
 
    bi.biSize = sizeof(BITMAPINFOHEADER); 
    bi.biWidth = dwWidth;         // fill in width from parameter 
    bi.biHeight = dwHeight;       // fill in height from parameter 
    bi.biPlanes = 1;              // must be 1 
    bi.biBitCount = wBitCount;    // from parameter 
    bi.biCompression = BI_RGB;     
    bi.biSizeImage = 0;           // 0's here mean "default" 
    bi.biXPelsPerMeter = 0; 
    bi.biYPelsPerMeter = 0; 
    bi.biClrUsed = 0; 
    bi.biClrImportant = 0; 
 
    // calculate size of memory block required to store the DIB.  This 
    // block should be big enough to hold the BITMAPINFOHEADER, the color 
    // table, and the bits 
 
    dwBytesPerLine = WIDTHBYTES(wBitCount * dwWidth); 
    dwLen = bi.biSize + PaletteSize((LPBYTE)&bi) + (dwBytesPerLine * dwHeight); 
 
    // alloc memory block to store our bitmap 
 
    hDIB = GlobalAlloc(GHND, dwLen); 
 
    // major bummer if we couldn't get memory block 
 
    if (!hDIB) 
        return NULL; 
 
    // lock memory and get pointer to it 
 
    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); 
 
    // use our bitmap info structure to fill in first part of 
    // our DIB with the BITMAPINFOHEADER 
 
    *lpbi = bi; 
 
    // Since we don't know what the colortable and bits should contain, 
    // just leave these blank.  Unlock the DIB and return the HDIB. 
 
    GlobalUnlock(hDIB); 
 
    //return handle to the DIB 
 
    return hDIB; 
} 
 
/************************************************************************* 
 * 
 * CreateDefaultDIB() 
 * 
 * Parameters: 
 * 
 * DWORD dwWidth    - Width for new bitmap, in pixels 
 * DWORD dwHeight   - Height for new bitmap  
  * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function allocates memory for and initializes a new DIB by 
 * filling in the BITMAPINFOHEADER, allocating memory for the color 
 * table, and allocating memory for the bitmap bits.  As with all 
 * HDIBs, the header, colortable and bits are all in one contiguous 
 * memory block.  This function is similar to the CreateBitmap()  
 * Windows API. 
 * 
 * The colortable is initialized with system palette, but bitmap bits
 * are left uninitialized (zeroed) in the returned HDIB. 
 * 
 * 
 ************************************************************************/ 
HDIB CreateDefaultDIB(DWORD dwWidth, DWORD dwHeight) 
{ 
	// Get DC
    HDC hDC = GetDC(NULL); 
    if (!hDC) 
        return NULL; 

	// DC bts/pixel
	int nDeviceBitsPixel = GetDeviceCaps(hDC, BITSPIXEL);

	// create DIB according to DC
	HDIB hDIB = CreateDIB(dwWidth, dwHeight, nDeviceBitsPixel);

	// DIB buffer
	LPBITMAPINFO lpbmi = (LPBITMAPINFO)GlobalLock(hDIB);
    LPBYTE lpDIBBits = FindDIBBits((LPBYTE)lpbmi); 
    DWORD dwBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 

	// set DIB color to White
	for (DWORD l=0; l<dwBitsSize; ++l)
		lpDIBBits[l] = 0xff;

	// if no palette, return DIB handle
	if (nDeviceBitsPixel > 8)
	{
		GlobalUnlock(hDIB);
	    ReleaseDC(NULL, hDC); 
		return hDIB;
	}

	// if there is palette, set system palette to DIB

	// colors in system palette
    int nColors = PalEntriesOnDevice(hDC);   // Number of palette entries 
  
    // Copy the current system palette into our logical palette 
	PALETTEENTRY pe[256];
    GetSystemPaletteEntries(hDC, 0, nColors, pe); 

	// set color table
	for (int i=0; i<nColors; ++i)
	{
		lpbmi->bmiColors[i].rgbRed = pe[i].peRed;
		lpbmi->bmiColors[i].rgbGreen = pe[i].peGreen;
		lpbmi->bmiColors[i].rgbBlue = pe[i].peBlue;
		lpbmi->bmiColors[i].rgbReserved = 0;
	}

    // clean up 
	GlobalUnlock(hDIB);
    ReleaseDC(NULL, hDC); 

	return hDIB;
}
/************************************************************************* 
 * 
 * DestroyDIB () 
 * 
 * Purpose:  Frees memory associated with a DIB 
 * 
 * Returns:  none 
 * 
 *************************************************************************/ 
void DestroyDIB(HDIB hDib) 
{ 
    GlobalFree(hDib); 
} 
 
/************************************************************************* 
 * 
 * LoadDIB() 
 * 
 * Loads the specified DIB from a file, allocates memory for it, 
 * and reads the disk file into the memory. 
 * 
 * 
 * Parameters: 
 * 
 * LPCTSTR lpFileName - specifies the file to load a DIB from 
 * 
 * Returns: A handle to a DIB, or NULL if unsuccessful. 
 * 
 * NOTE: The DIB API were not written to handle OS/2 DIBs; This 
 * function will reject any file that is not a Windows DIB. 
 * 
 *************************************************************************/ 
HDIB LoadDIB(LPCTSTR lpFileName) 
{ 
    HDIB        hDIB; 
    HANDLE      hFile; 
 
    // Set the cursor to a hourglass, in case the loading operation 
    // takes more than a sec, the user will know what's going on. 
 
    SetCursor(LoadCursor(NULL, IDC_WAIT)); 
 
    if ((hFile = CreateFile(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL, 
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 
            NULL)) != INVALID_HANDLE_VALUE) 
    { 
        hDIB = ReadDIBFile(hFile); 
        CloseHandle(hFile); 
        SetCursor(LoadCursor(NULL, IDC_ARROW)); 
        return hDIB; 
    } 
    else 
    { 
        SetCursor(LoadCursor(NULL, IDC_ARROW)); 
        return NULL; 
    } 
} 
 
 
/************************************************************************* 
 * 
 * SaveDIB() 
 * 
 * Saves the specified DIB into the specified file name on disk.  No 
 * error checking is done, so if the file already exists, it will be 
 * written over. 
 * 
 * Parameters: 
 * 
 * HDIB hDib - Handle to the dib to save 
 * 
 * LPCTSTR lpFileName - pointer to full pathname to save DIB under 
 * 
 * Return value: TRUE if successful
 *				 FALSE if failure
 * 
 *************************************************************************/ 
BOOL SaveDIB(HDIB hDib, LPCTSTR lpFileName) 
{ 
    HANDLE              fh;         // file handle for opened file 
 
    if (!hDib) 
        return FALSE; 
 
    fh = CreateFile(lpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); 
 
    if (fh == INVALID_HANDLE_VALUE) 
        return FALSE; 

	BOOL bSuccess = WriteDIBFile(hDib, fh);

    CloseHandle(fh);
	return bSuccess;
}

/************************************************************************* 
 * 
 * Function:  ReadDIBFile (int) 
 * 
 *  Purpose:  Reads in the specified DIB file into a global chunk of 
 *            memory. 
 * 
 *  Returns:  A handle to a dib (hDIB) if successful. 
 *            NULL if an error occurs. 
 * 
 * Comments:  BITMAPFILEHEADER is stripped off of the DIB.  Everything 
 *            from the end of the BITMAPFILEHEADER structure on is 
 *            returned in the global memory handle. 
 * 
 * 
 * NOTE: The DIB API were not written to handle OS/2 DIBs, so this 
 * function will reject any file that is not a Windows DIB. 
 * 
 *************************************************************************/ 
HANDLE ReadDIBFile(HANDLE hFile) 
{ 
    BITMAPFILEHEADER    bmfHeader; 
    DWORD               dwBitsSize; 
    UINT                nNumColors;   // Number of colors in table 
    HANDLE              hDIB;         
    HANDLE              hDIBtmp;      // Used for GlobalRealloc() //MPB 
    LPBITMAPINFOHEADER  lpbi; 
    DWORD               offBits; 
    DWORD               dwRead; 
 
    // get length of DIB in bytes for use when reading 
 
    dwBitsSize = GetFileSize(hFile, NULL); 
 
    // Allocate memory for header & color table. We'll enlarge this 
    // memory as needed. 
 
    hDIB = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(sizeof(BITMAPINFOHEADER) + 
            256 * sizeof(RGBQUAD))); 
 
    if (!hDIB) 
        return NULL; 
 
    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); 
 
    if (!lpbi)  
    { 
        GlobalFree(hDIB); 
        return NULL; 
    } 
 
    // read the BITMAPFILEHEADER from our file 
 
    if (!ReadFile(hFile, (LPBYTE)&bmfHeader, sizeof (BITMAPFILEHEADER), 
            &dwRead, NULL)) 
        goto ErrExit; 
 
    if (sizeof (BITMAPFILEHEADER) != dwRead) 
        goto ErrExit; 
 
    if (bmfHeader.bfType != 0x4d42)  // 'BM' 
        goto ErrExit; 
 
    // read the BITMAPINFOHEADER 
 
    if (!ReadFile(hFile, (LPBYTE)lpbi, sizeof(BITMAPINFOHEADER), &dwRead, 
            NULL)) 
        goto ErrExit; 
 
    if (sizeof(BITMAPINFOHEADER) != dwRead) 
        goto ErrExit; 
 
    // Check to see that it's a Windows DIB -- an OS/2 DIB would cause 
    // strange problems with the rest of the DIB API since the fields 
    // in the header are different and the color table entries are 
    // smaller. 
    // 
    // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL. 
 
    if (lpbi->biSize == sizeof(BITMAPCOREHEADER)) 
        goto ErrExit; 
 
    // Now determine the size of the color table and read it.  Since the 
    // bitmap bits are offset in the file by bfOffBits, we need to do some 
    // special processing here to make sure the bits directly follow 
    // the color table (because that's the format we are susposed to pass 
    // back) 
 
    if (!(nNumColors = (UINT)lpbi->biClrUsed)) 
    { 
        // no color table for 24-bit, default size otherwise 
 
        if (lpbi->biBitCount > 8)	//(lpbi->biBitCount != 24) 
            nNumColors = 1 << lpbi->biBitCount; // standard size table 
    } 
 
    // fill in some default values if they are zero 
 
    if (lpbi->biClrUsed == 0) 
        lpbi->biClrUsed = nNumColors; 
 
    if (lpbi->biSizeImage == 0) 
    { 
        lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 
                31) & ~31) >> 3) * lpbi->biHeight; 
    } 
 
    // get a proper-sized buffer for header, color table and bits 
 
    GlobalUnlock(hDIB); 
    hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize + nNumColors * 
            sizeof(RGBQUAD) + lpbi->biSizeImage, 0); 
 
    if (!hDIBtmp) // can't resize buffer for loading 
        goto ErrExitNoUnlock; //MPB 
    else 
        hDIB = hDIBtmp; 
 
    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); 
 
    // read the color table 
 
    ReadFile (hFile, (LPBYTE)(lpbi) + lpbi->biSize, 
            nNumColors * sizeof(RGBQUAD), &dwRead, NULL); 
 
    // offset to the bits from start of DIB header 
 
    offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD); 
 
    // If the bfOffBits field is non-zero, then the bits might *not* be 
    // directly following the color table in the file.  Use the value in 
    // bfOffBits to seek the bits. 
 
    if (bmfHeader.bfOffBits != 0L) 
        SetFilePointer(hFile, bmfHeader.bfOffBits, NULL, FILE_BEGIN); 
 
    if (ReadFile(hFile, (LPBYTE)lpbi + offBits, lpbi->biSizeImage, &dwRead, 
            NULL)) 
        goto OKExit; 
 
ErrExit: 
    GlobalUnlock(hDIB);     
 
ErrExitNoUnlock:     
    GlobalFree(hDIB); 
    return NULL; 
 
OKExit: 
    GlobalUnlock(hDIB); 
    return hDIB; 
} 

/************************************************************************* 
 * 
 * Function:  WriteDIBFile (HANDLE, int) 
 * 
 *  Purpose:  Writes a global chunk of DIB memory to specified file 
 * 
 *  Returns:  TRUE if success, else FALSE
 * 
 * Comments:  BITMAPFILEHEADER is stripped off of the DIB.  Everything 
 *            from the end of the BITMAPFILEHEADER structure on is 
 *            returned in the global memory handle. 
 * 
 * 
 * NOTE: The DIB API were not written to handle OS/2 DIBs, so this 
 * function will reject any file that is not a Windows DIB. 
 * 
 *************************************************************************/ 
BOOL WriteDIBFile(HDIB hDib, HANDLE fh) 
{ 
    BITMAPFILEHEADER    bmfHdr;     // Header for Bitmap file 
    LPBITMAPINFOHEADER  lpBI;       // Pointer to DIB info structure 
    DWORD               dwDIBSize; 
    DWORD               dwWritten; 

    if (!hDib) 
        return FALSE; 

    // Get a pointer to the DIB memory, the first of which contains 
    // a BITMAPINFO structure 
 
    lpBI = (LPBITMAPINFOHEADER)GlobalLock(hDib); 
    if (!lpBI) 
    { 
        CloseHandle(fh); 
        return FALSE; 
    } 
 
    // Check to see if we're dealing with an OS/2 DIB.  If so, don't 
    // save it because our functions aren't written to deal with these 
    // DIBs. 
 
    if (lpBI->biSize != sizeof(BITMAPINFOHEADER)) 
    { 
        GlobalUnlock(hDib); 
        CloseHandle(fh); 
        return FALSE; 
    } 
 
    // Fill in the fields of the file header 
 
    // Fill in file type (first 2 bytes must be "BM" for a bitmap) 
 
    bmfHdr.bfType = DIB_HEADER_MARKER;  // "BM" 
 
    // Calculating the size of the DIB is a bit tricky (if we want to 
    // do it right).  The easiest way to do this is to call GlobalSize() 
    // on our global handle, but since the size of our global memory may have 
    // been padded a few bytes, we may end up writing out a few too 
    // many bytes to the file (which may cause problems with some apps, 
    // like HC 3.0). 
    // 
    // So, instead let's calculate the size manually. 
    // 
    // To do this, find size of header plus size of color table.  Since the 
    // first DWORD in both BITMAPINFOHEADER and BITMAPCOREHEADER conains 
    // the size of the structure, let's use this. 
 
    // Partial Calculation 
 
    dwDIBSize = *(LPDWORD)lpBI + PaletteSize((LPBYTE)lpBI);   
 
    // Now calculate the size of the image 
 
    // It's an RLE bitmap, we can't calculate size, so trust the biSizeImage 
    // field 
 
    if ((lpBI->biCompression == BI_RLE8) || (lpBI->biCompression == BI_RLE4)) 
        dwDIBSize += lpBI->biSizeImage; 
    else 
    { 
        DWORD dwBmBitsSize;  // Size of Bitmap Bits only 
 
        // It's not RLE, so size is Width (DWORD aligned) * Height 
 
        dwBmBitsSize = WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) * 
                lpBI->biHeight; 
 
        dwDIBSize += dwBmBitsSize; 
 
        // Now, since we have calculated the correct size, why don't we 
        // fill in the biSizeImage field (this will fix any .BMP files which  
        // have this field incorrect). 
 
        lpBI->biSizeImage = dwBmBitsSize; 
    } 
 
 
    // Calculate the file size by adding the DIB size to sizeof(BITMAPFILEHEADER) 
                    
    bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER); 
    bmfHdr.bfReserved1 = 0; 
    bmfHdr.bfReserved2 = 0; 
 
    // Now, calculate the offset the actual bitmap bits will be in 
    // the file -- It's the Bitmap file header plus the DIB header, 
    // plus the size of the color table. 
     
    bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize + 
            PaletteSize((LPBYTE)lpBI); 
 
    // Write the file header 
 
    WriteFile(fh, (LPBYTE)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL); 
 
    // Write the DIB header and the bits -- use local version of 
    // MyWrite, so we can write more than 32767 bytes of data 
     
    WriteFile(fh, (LPBYTE)lpBI, dwDIBSize, &dwWritten, NULL); 
 
    GlobalUnlock(hDib); 
 
    if (dwWritten == 0) 
        return FALSE; // oops, something happened in the write 
    else 
        return TRUE; // Success code 
} 
 
/************************************************************************* 
 * 
 * FindDIBBits() 
 * 
 * Parameter: 
 * 
 * LPBYTE lpDIB      - pointer to packed-DIB memory block 
 * 
 * Return Value: 
 * 
 * LPBYTE            - pointer to the DIB bits 
 * 
 * Description: 
 * 
 * This function calculates the address of the DIB's bits and returns a 
 * pointer to the DIB bits. 
 * 
 ************************************************************************/ 
LPBYTE FindDIBBits(LPBYTE lpDIB) 
{ 
   return (lpDIB + *(LPDWORD)lpDIB + PaletteSize(lpDIB)); 
} 
// *(LPDWORD)lpDIB即为lpDIB指针指向的首地址的内容,而此内容正好是BITMAPINFOHEADER
// 结构的大小,即biSize,见BIPMAPINFOHEADER结构的定义
/************************************************************************* 
 * 
 * DIBWidth() 
 * 
 * Parameter: 
 * 
 * LPBYTE lpDIB      - pointer to packed-DIB memory block 
 * 
 * Return Value: 
 * 
 * DWORD            - width of the DIB 
 * 
 * Description: 
 * 
 * This function gets the width of the DIB from the BITMAPINFOHEADER 
 * width field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER 
 * width field if it is an OS/2-style DIB. 
 * 
 ************************************************************************/ 
DWORD DIBWidth(LPBYTE lpDIB) 
{ 
    LPBITMAPINFOHEADER   lpbmi;  // pointer to a Win 3.0-style DIB 
    LPBITMAPCOREHEADER   lpbmc;  // pointer to an OS/2-style DIB 
 
    // point to the header (whether Win 3.0 and OS/2) 
 
    lpbmi = (LPBITMAPINFOHEADER)lpDIB; 
    lpbmc = (LPBITMAPCOREHEADER)lpDIB; 
 
    // return the DIB width if it is a Win 3.0 DIB 
 
    if (lpbmi->biSize == sizeof(BITMAPINFOHEADER)) 
        return lpbmi->biWidth; 
    else  // it is an OS/2 DIB, so return its width 
        return (DWORD)lpbmc->bcWidth; 
} 
 
DWORD DIBWidth(HDIB hDIB) 
{ 
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hDIB);
	DWORD dw = DIBWidth(lpDIB);
	GlobalUnlock(hDIB);
	return dw;
} 
 
/************************************************************************* 
 * 
 * DIBHeight() 
 * 
 * Parameter: 
 * 
 * LPBYTE lpDIB      - pointer to packed-DIB memory block 
 * 
 * Return Value: 
 * 
 * DWORD            - height of the DIB 
 * 
 * Description: 
 * 
 * This function gets the height of the DIB from the BITMAPINFOHEADER 
 * height field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER 
 * height field if it is an OS/2-style DIB. 
 * 
 ************************************************************************/ 
DWORD DIBHeight(LPBYTE lpDIB) 
{ 
   LPBITMAPINFOHEADER   lpbmi;  // pointer to a Win 3.0-style DIB 
   LPBITMAPCOREHEADER   lpbmc;  // pointer to an OS/2-style DIB 
 
   // point to the header (whether OS/2 or Win 3.0 
 
   lpbmi = (LPBITMAPINFOHEADER)lpDIB; 
   lpbmc = (LPBITMAPCOREHEADER)lpDIB; 
 
    // return the DIB height if it is a Win 3.0 DIB 
    if (lpbmi->biSize == sizeof(BITMAPINFOHEADER)) 
        return lpbmi->biHeight; 
    else  // it is an OS/2 DIB, so return its height 
        return (DWORD)lpbmc->bcHeight; 
} 
 
DWORD DIBHeight(HDIB hDIB) 
{ 
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hDIB);
	DWORD dw = DIBHeight(lpDIB);
	GlobalUnlock(hDIB);
	return dw;
} 
 
/************************************************************************* 
 * 
 * PaletteSize() 
 * 
 * Parameter: 
 * 
 * LPBYTE lpDIB      - pointer to packed-DIB memory block 
 * 
 * Return Value: 
 * 
 * WORD             - size of the color palette of the DIB 
 * 
 * Description: 
 * 
 * This function gets the size required to store the DIB's palette by 
 * multiplying the number of colors by the size of an RGBQUAD (for a 
 * Windows 3.0-style DIB) or by the size of an RGBTRIPLE (for an OS/2- 
 * style DIB). 
 * 
 ************************************************************************/ 
WORD PaletteSize(LPBYTE lpDIB) 
{ 
    // calculate the size required by the palette 
    if (IS_WIN30_DIB (lpDIB)) 
        return (DIBNumColors(lpDIB) * sizeof(RGBQUAD)); 
    else 
        return (DIBNumColors(lpDIB) * sizeof(RGBTRIPLE)); 
} 
 
WORD PaletteSize(HDIB hDIB) 
{ 
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hDIB);
	WORD wSize = PaletteSize(lpDIB);
	GlobalUnlock(hDIB);
	return wSize;
} 
 
/************************************************************************* 
 * 
 * DIBNumColors() 
 * 
 * Parameter: 
 * 
 * LPBYTE lpDIB      - pointer to packed-DIB memory block 
 * 
 * Return Value: 
 * 
 * WORD             - number of colors in the color table 
 * 
 * Description: 
 * 
 * This function calculates the number of colors in the DIB's color table 
 * by finding the bits per pixel for the DIB (whether Win3.0 or OS/2-style 
 * DIB). If bits per pixel is 1: colors=2, if 4: colors=16, if 8: colors=256, 
 * if 24, no colors in color table. 
 * 
 ************************************************************************/ 
WORD DIBNumColors(LPBYTE lpDIB) 
{ 
    WORD wBitCount;  // DIB bit count 
 
    // If this is a Windows-style DIB, the number of colors in the 
    // color table can be less than the number of bits per pixel 
    // allows for (i.e. lpbi->biClrUsed can be set to some value). 
    // If this is the case, return the appropriate value. 
     
 
    if (IS_WIN30_DIB(lpDIB)) 
    { 
        DWORD dwClrUsed; 
 
        dwClrUsed = ((LPBITMAPINFOHEADER)lpDIB)->biClrUsed; 
        if (dwClrUsed) 
 
        return (WORD)dwClrUsed; 
    } 
 
    // Calculate the number of colors in the color table based on 
    // the number of bits per pixel for the DIB. 
     
    if (IS_WIN30_DIB(lpDIB)) 
        wBitCount = ((LPBITMAPINFOHEADER)lpDIB)->biBitCount; 
    else 
        wBitCount = ((LPBITMAPCOREHEADER)lpDIB)->bcBitCount; 
 
    // return number of colors based on bits per pixel 
 
    switch (wBitCount) 
    { 
        case 1: 
            return 2; 
 
        case 4: 
            return 16; 
 
        case 8: 
            return 256; 
 
        default: 
            return 0; 
    } 
} 

WORD DIBNumColors(HDIB hDIB) 
{ 
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hDIB);
	WORD wSize = DIBNumColors(lpDIB);
	GlobalUnlock(hDIB);
	return wSize;
} 

WORD DIBBitCount(LPBYTE lpDIB) 
{ 
    if (IS_WIN30_DIB(lpDIB)) 
        return ((LPBITMAPINFOHEADER)lpDIB)->biBitCount; 
    else 
        return ((LPBITMAPCOREHEADER)lpDIB)->bcBitCount; 
} 

WORD DIBBitCount(HDIB hDIB) 
{ 
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hDIB);
	WORD wSize = DIBBitCount(lpDIB);
	GlobalUnlock(hDIB);
	return wSize;
} 
 
/**************************************************************************** 
* 
*     FUNCTION: BytesPerLine 
* 
*     PURPOSE:  Calculates the number of bytes in one scan line. 
* 
*     PARAMS:   LPBYTE lpDIB - pointer to the BITMAPINFOHEADER 
                              that begins the CF_DIB block 
* 
*     RETURNS:  DWORD - number of bytes in one scan line (DWORD aligned) 
* 
\****************************************************************************/ 
DWORD BytesPerLine(LPBYTE lpDIB) 
{ 
    return WIDTHBYTES(((LPBITMAPINFOHEADER)lpDIB)->biWidth * ((LPBITMAPINFOHEADER)lpDIB)->biPlanes * ((LPBITMAPINFOHEADER)lpDIB)->biBitCount); 
} 

DWORD BytesPerLine(HDIB hDIB) 
{ 
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hDIB);
	DWORD dw = BytesPerLine(lpDIB);
	GlobalUnlock(hDIB);
	return dw;
} 

/**************************************************************************** 
* 
*     FUNCTION: DIBlockSize 
* 
*     PURPOSE:  Calculates the number of bytes in one scan line. 
* 
*     PARAMS:   LPBYTE lpDIB - pointer to the BITMAPINFOHEADER 
                              that begins the CF_DIB block 
* 
*     RETURNS:  DWORD - DIB block size
* 
\****************************************************************************/ 
DWORD DIBlockSize(LPBYTE lpDIB)
{
	if (((LPBITMAPINFOHEADER)lpDIB)->biSizeImage == 0)
		((LPBITMAPINFOHEADER)lpDIB)->biSizeImage = BytesPerLine(lpDIB) * ((LPBITMAPINFOHEADER)lpDIB)->biHeight;
	return ((LPBITMAPINFOHEADER)lpDIB)->biSize + PaletteSize(lpDIB) + ((LPBITMAPINFOHEADER)lpDIB)->biSizeImage;
}

DWORD DIBlockSize(HDIB hDIB) 
{ 
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hDIB);
	DWORD dw = DIBlockSize(lpDIB);
	GlobalUnlock(hDIB);
	return dw;
} 
 
/************************************************************************* 
 * 
 * CreateDIBPalette() 
 * 
 * Parameter: 
 * 
 * LPBYTE lpbi       - specifies the DIB 
 * 
 * Return Value: 
 * 
 * HPALETTE         - specifies the palette 
 * 
 * Description: 
 * 
 * This function creates a palette from a DIB by allocating memory for the 
 * logical palette, reading and storing the colors from the DIB's color table 
 * into the logical palette, creating a palette from this logical palette, 
 * and then returning the palette's handle. This allows the DIB to be 
 * displayed using the best possible colors (important for DIBs with 256 or 
 * more colors). 
 * 
 ************************************************************************/ 
HPALETTE CreateDIBPalette(LPBYTE lpbi) 
{ 
    LPLOGPALETTE        lpPal;          // pointer to a logical palette 
    HANDLE              hLogPal;        // handle to a logical palette 
    HPALETTE            hPal = NULL;    // handle to a palette 
    int                 i, wNumColors;  // loop index, number of colors in color table 
    LPBITMAPINFO        lpbmi;          // pointer to BITMAPINFO structure (Win3.0) 
    LPBITMAPCOREINFO    lpbmc;          // pointer to BITMAPCOREINFO structure (OS/2) 
    BOOL                bWinStyleDIB;   // Win3.0 DIB? 
 
    // if handle to DIB is invalid, return NULL 
 
    if (! lpbi) 
        return NULL; 
 
    // get pointer to BITMAPINFO (Win 3.0) 
 
    lpbmi = (LPBITMAPINFO)lpbi; 
 
    // get pointer to BITMAPCOREINFO (OS/2 1.x) 
 
    lpbmc = (LPBITMAPCOREINFO)lpbi; 
 
    // get the number of colors in the DIB  
 
    wNumColors = DIBNumColors(lpbi); 
 
    // is this a Win 3.0 DIB? 
 
    bWinStyleDIB = IS_WIN30_DIB(lpbi); 
    if (wNumColors) 
    { 
        // allocate memory block for logical palette 
 
        hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + 
                sizeof(PALETTEENTRY) * wNumColors); 
 
        // if not enough memory, clean up and return NULL 
 
        if (!hLogPal) 
            return NULL; 
 
        // lock memory block and get pointer to it 
 
        lpPal = (LPLOGPALETTE)GlobalLock(hLogPal); 
 
        // set version and number of palette entries 
 
        lpPal->palVersion = PALVERSION; 
        lpPal->palNumEntries = wNumColors; 
 
        // store RGB triples (if Win 3.0 DIB) or RGB quads (if OS/2 DIB) 
        // into palette 
         
        for (i = 0; i < wNumColors; i++) 
        { 
            if (bWinStyleDIB) 
            { 
                lpPal->palPalEntry[i].peRed = lpbmi->bmiColors[i].rgbRed; 
                lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen; 
                lpPal->palPalEntry[i].peBlue = lpbmi->bmiColors[i].rgbBlue; 
                lpPal->palPalEntry[i].peFlags = 0; 
            } 
            else 
            { 
                lpPal->palPalEntry[i].peRed = lpbmc->bmciColors[i].rgbtRed; 
                lpPal->palPalEntry[i].peGreen = lpbmc->bmciColors[i].rgbtGreen; 
                lpPal->palPalEntry[i].peBlue = lpbmc->bmciColors[i].rgbtBlue; 
                lpPal->palPalEntry[i].peFlags = 0; 
            } 
        } 
 
        // create the palette and get handle to it 
 
        hPal = CreatePalette(lpPal); 
 
        // cleanup
 
        GlobalUnlock(hLogPal); 
        GlobalFree(hLogPal); 
    } 
 
    // return handle to DIB's palette 
    return hPal; 
} 
 
/************************************************************************* 
 * 
 * CreateDIBPalette() 
 * 
 * Parameter: 
 * 
 * HDIB hDIB        - specifies the DIB 
 * 
 * Return Value: 
 * 
 * HPALETTE         - specifies the palette 
 * 
 * Description: 
 * 
 * This function creates a palette from a DIB by allocating memory for the 
 * logical palette, reading and storing the colors from the DIB's color table 
 * into the logical palette, creating a palette from this logical palette, 
 * and then returning the palette's handle. This allows the DIB to be 
 * displayed using the best possible colors (important for DIBs with 256 or 
 * more colors). 
 * 
 ************************************************************************/ 
HPALETTE CreateDIBPalette(HDIB hDIB) 
{ 
    HPALETTE            hPal = NULL;    // handle to a palette 
    LPBYTE               lpbi;           // pointer to packed-DIB 
 
    // if handle to DIB is invalid, return NULL 
 
    if (!hDIB) 
        return NULL; 
 
    // lock DIB memory block and get a pointer to it 
 
    lpbi = (LPBYTE)GlobalLock(hDIB); 
 
	hPal = CreateDIBPalette(lpbi);

	// Unlock hDIB
    GlobalUnlock(hDIB); 
 
    // return handle to DIB's palette 
    return hPal; 
} 
 
/************************************************************************* 
 * 
 * DIBToBitmap() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB        - specifies the DIB to convert 
 * 
 * HPALETTE hPal    - specifies the palette to use with the bitmap 
 * 
 * Return Value: 
 * 
 * HBITMAP          - identifies the device-dependent bitmap 
 * 
 * Description: 
 * 
 * This function creates a bitmap from a DIB using the specified palette. 
 * If no palette is specified, default is used. 
 * 
 * NOTE: 
 * 
 * The bitmap returned from this funciton is always a bitmap compatible 
 * with the screen (e.g. same bits/pixel and color planes) rather than 
 * a bitmap with the same attributes as the DIB.  This behavior is by 
 * design, and occurs because this function calls CreateDIBitmap to 
 * do its work, and CreateDIBitmap always creates a bitmap compatible 
 * with the hDC parameter passed in (because it in turn calls 
 * CreateCompatibleBitmap). 
 * 
 * So for instance, if your DIB is a monochrome DIB and you call this 
 * function, you will not get back a monochrome HBITMAP -- you will 
 * get an HBITMAP compatible with the screen DC, but with only 2 
 * colors used in the bitmap. 
 * 
 * If your application requires a monochrome HBITMAP returned for a 
 * monochrome DIB, use the function SetDIBits(). 
 * 
 * Also, the DIBpassed in to the function is not destroyed on exit. This 
 * must be done later, once it is no longer needed. 
 * 
 ************************************************************************/ 
HBITMAP DIBToBitmap(HDIB hDIB, HPALETTE hPal) 
{ 
    LPBYTE       lpDIBHdr, lpDIBBits;  // pointer to DIB header, pointer to DIB bits 
    HBITMAP     hBitmap;            // handle to device-dependent bitmap 
    HDC         hDC;                    // handle to DC 
    HPALETTE    hOldPal = NULL;    // handle to a palette 
 
    // if invalid handle, return NULL  
 
    if (!hDIB) 
        return NULL; 
 
    // lock memory block and get a pointer to it 
 
    lpDIBHdr = (LPBYTE)GlobalLock(hDIB); 
 
    // get a pointer to the DIB bits 
 
    lpDIBBits = FindDIBBits(lpDIBHdr); 
 
    // get a DC  
 
    hDC = GetDC(NULL); 
    if (!hDC) 
    { 
        // clean up and return NULL 
        GlobalUnlock(hDIB); 
        return NULL; 
    } 
 
    // select and realize palette 
 
    if (hPal) 
	{
        hOldPal = SelectPalette(hDC, hPal, FALSE); 
	    RealizePalette(hDC); 
	}
 
    // create bitmap from DIB info and bits 
    hBitmap = CreateDIBitmap(hDC, 
							(LPBITMAPINFOHEADER)lpDIBHdr, 
							CBM_INIT, 
							lpDIBBits, 
							(LPBITMAPINFO)lpDIBHdr, 
							DIB_RGB_COLORS);
 
    // restore previous palette 
    if (hOldPal) 
        SelectPalette(hDC, hOldPal, FALSE); 
 
    // clean up 
    ReleaseDC(NULL, hDC); 
    GlobalUnlock(hDIB); 
 
    // return handle to the bitmap 
    return hBitmap; 
} 
 
 
/************************************************************************* 
 * 
 * BitmapToDIB() 
 * 
 * Parameters: 
 * 
 * HBITMAP hBitmap  - specifies the bitmap to convert 
 * 
 * HPALETTE hPal    - specifies the palette to use with the bitmap 
 * 
 * Return Value: 
 * 
 * HDIB             - identifies the device-dependent bitmap 
 * 
 * Description: 
 * 
 * This function creates a DIB from a bitmap using the specified palette. 
 * 
 ************************************************************************/ 
HDIB BitmapToDIB(HBITMAP hBitmap, HPALETTE hPal) 
{ 
    BITMAP              bm;         // bitmap structure 
    BITMAPINFOHEADER    bi;         // bitmap header 
    LPBITMAPINFOHEADER  lpbi;       // pointer to BITMAPINFOHEADER 
    DWORD               dwLen;      // size of memory block 
    HANDLE              hDIB, h;    // handle to DIB, temp handle 
    HDC                 hDC;        // handle to DC 
    WORD                biBits;     // bits per pixel 
 
    // check if bitmap handle is valid 
 
    if (!hBitmap) 
        return NULL; 
 
    // fill in BITMAP structure, return NULL if it didn't work 
 
    if (!GetObject(hBitmap, sizeof(bm), (LPBYTE)&bm)) 
        return NULL; 
 
    // if no palette is specified, use default palette 
 
    if (hPal == NULL) 
        hPal = (HPALETTE)GetStockObject(DEFAULT_PALETTE); 
 
    // calculate bits per pixel 
 
    biBits = bm.bmPlanes * bm.bmBitsPixel; 
 
    // make sure bits per pixel is valid 
 
    if (biBits <= 1) 
        biBits = 1; 
    else if (biBits <= 4) 
        biBits = 4; 
    else if (biBits <= 8) 
        biBits = 8; 
    else // if greater than 8-bit, force to 24-bit 
        biBits = 24; 
 
    // initialize BITMAPINFOHEADER 
 
    bi.biSize = sizeof(BITMAPINFOHEADER); 
    bi.biWidth = bm.bmWidth; 
    bi.biHeight = bm.bmHeight; 
    bi.biPlanes = 1; 
    bi.biBitCount = biBits; 
    bi.biCompression = BI_RGB; 
    bi.biSizeImage = 0; 
    bi.biXPelsPerMeter = 0; 
    bi.biYPelsPerMeter = 0; 
    bi.biClrUsed = 0; 
    bi.biClrImportant = 0; 
 
    // calculate size of memory block required to store BITMAPINFO 
 
    dwLen = bi.biSize + PaletteSize((LPBYTE)&bi); 
 
    // get a DC 
 
    hDC = GetDC(NULL); 
 
    // select and realize our palette 
 
    hPal = SelectPalette(hDC, hPal, FALSE); 
    RealizePalette(hDC); 
 
    // alloc memory block to store our bitmap 
 
    hDIB = GlobalAlloc(GHND, dwLen); 
 
    // if we couldn't get memory block 
 
    if (!hDIB) 
    { 
      // clean up and return NULL 
 
      SelectPalette(hDC, hPal, TRUE); 
      RealizePalette(hDC); 
      ReleaseDC(NULL, hDC); 
      return NULL; 
    } 
 
    // lock memory and get pointer to it 
 
    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); 
 
    /// use our bitmap info. to fill BITMAPINFOHEADER 
 
    *lpbi = bi; 
 
    // call GetDIBits with a NULL lpBits param, so it will calculate the 
    // biSizeImage field for us     
 
    GetDIBits(hDC, hBitmap, 0, (UINT)bi.biHeight, NULL, (LPBITMAPINFO)lpbi, 
        DIB_RGB_COLORS); 
 
    // get the info. returned by GetDIBits and unlock memory block 
 
    bi = *lpbi; 
    GlobalUnlock(hDIB); 
 
    // if the driver did not fill in the biSizeImage field, make one up  
    if (bi.biSizeImage == 0) 
        bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight; 
 
    // realloc the buffer big enough to hold all the bits 
 
    dwLen = bi.biSize + PaletteSize((LPBYTE)&bi) + bi.biSizeImage; 
 
    if (h = GlobalReAlloc(hDIB, dwLen, 0)) 
        hDIB = h; 
    else 
    { 
        // clean up and return NULL 
 
        GlobalFree(hDIB); 
        hDIB = NULL; 
        SelectPalette(hDC, hPal, TRUE); 
        RealizePalette(hDC); 
        ReleaseDC(NULL, hDC); 
        return NULL; 
    } 
 
    // lock memory block and get pointer to it */ 
 
    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); 
 
    // call GetDIBits with a NON-NULL lpBits param, and actualy get the 
    // bits this time 
 
    if (GetDIBits(hDC, hBitmap, 0, (UINT)bi.biHeight, (LPBYTE)lpbi + 
            (WORD)lpbi->biSize + PaletteSize((LPBYTE)lpbi), (LPBITMAPINFO)lpbi, 
            DIB_RGB_COLORS) == 0) 
    { 
        // clean up and return NULL 
 
        GlobalUnlock(hDIB); 
        hDIB = NULL; 
        SelectPalette(hDC, hPal, TRUE); 
        RealizePalette(hDC); 
        ReleaseDC(NULL, hDC); 
        return NULL; 
    } 
 
    bi = *lpbi; 
 
    // clean up  
    GlobalUnlock(hDIB); 
    SelectPalette(hDC, hPal, TRUE); 
    RealizePalette(hDC); 
    ReleaseDC(NULL, hDC); 
 
    // return handle to the DIB 
    return hDIB; 
} 
 
/************************************************************************* 
 * 
 * BitmapToDIB() 
 * 
 * Parameters: 
 * 
 * HBITMAP hBitmap  - specifies the bitmap to convert 
 * 
 * HPALETTE hPal    - specifies the palette to use with the bitmap 
 * 
 * WORD wBitCount   - specifies the DIB colorbits 
 *
 * Return Value: 
 * 
 * HDIB             - identifies the device-dependent bitmap 
 * 
 * Description: 
 * 
 * This function creates a DIB from a bitmap using the specified palette. 
 * 
 ************************************************************************/ 
HDIB BitmapToDIB(HBITMAP hBitmap, HPALETTE hPalette, WORD wBitCount)
{ 
	HDIB hNewDib;

	if (! hBitmap)
        return NULL; 

	// get bitmap info
	BITMAP  bm;         // bitmap structure 
	GetObject(hBitmap, sizeof(bm), (LPBYTE)&bm); 
	int biBits = bm.bmPlanes * bm.bmBitsPixel;
	// make sure bits per pixel is valid 
	if (biBits <= 1) 
		biBits = 1; 
	else if (biBits <= 4) 
		biBits = 4; 
	else if (biBits <= 8) 
		biBits = 8; 
	else // if greater than 8-bit, force to 24-bit 
		biBits = 24; 

	// get DIB handle from current bitmap
	HDIB hDib = BitmapToDIB(hBitmap, hPalette);
	if (! hDib)
        return NULL; 

	if (wBitCount == biBits)
		hNewDib = hDib;
	else
	{
	    // DIB bits/pixel is not the same as device
		// convert dib format to accordingly
		hNewDib = ConvertDIBFormat(hDib, wBitCount, hPalette);
		// cleanup hDib
		GlobalFree(hDib);
	}

	return hNewDib;
} 


/************************************************************************* 
 * 
 * ChangeBitmapFormat() 
 * 
 * Parameter: 
 * 
 * HBITMAP          - handle to a bitmap 
 * 
 * WORD             - desired bits per pixel 
 * 
 * DWORD            - desired compression format 
 * 
 * HPALETTE         - handle to palette 
 * 
 * Return Value: 
 * 
 * HDIB             - handle to the new DIB if successful, else NULL 
 * 
 * Description: 
 * 
 * This function will convert a bitmap to the specified bits per pixel 
 * and compression format. The bitmap and it's palette will remain 
 * after calling this function. 
 * 
 ************************************************************************/ 
HDIB ChangeBitmapFormat (HBITMAP hbm, WORD biBits, DWORD biCompression, HPALETTE hpal)
{
    BITMAP               bm;
    BITMAPINFOHEADER     bi;
    LPBITMAPINFOHEADER   lpbi;
    DWORD                dwLen;
    HANDLE               hdib;
    HANDLE               h;
    HDC                  hdc;

    if (!hbm)
        return NULL;

    if (hpal == NULL)
        hpal = (HPALETTE)GetStockObject(DEFAULT_PALETTE);

    GetObject(hbm,sizeof(bm),(LPBYTE)&bm);

    if (biBits == 0)
	{
        biBits =  bm.bmPlanes * bm.bmBitsPixel;

		// make sure bits per pixel is valid 
	    if (biBits <= 1) 
		    biBits = 1; 
	    else if (biBits <= 4) 
		    biBits = 4; 
	    else if (biBits <= 8) 
		    biBits = 8; 
		else // if greater than 8-bit, force to 24-bit 
			biBits = 24; 
	}

    bi.biSize               = sizeof(BITMAPINFOHEADER);
    bi.biWidth              = bm.bmWidth;
    bi.biHeight             = bm.bmHeight;
    bi.biPlanes             = 1;
    bi.biBitCount           = biBits;
    bi.biCompression        = biCompression;
    bi.biSizeImage          = 0;
    bi.biXPelsPerMeter      = 0;
    bi.biYPelsPerMeter      = 0;
    bi.biClrUsed            = 0;
    bi.biClrImportant       = 0;

    dwLen  = bi.biSize + PaletteSize((LPBYTE)&bi);

    hdc = GetDC(NULL);
    HPALETTE hpalT = SelectPalette(hdc,hpal,FALSE);
    RealizePalette(hdc);

    hdib = GlobalAlloc(GHND,dwLen);

    if (!hdib)
	{
        SelectPalette(hdc,hpalT,FALSE);
        ReleaseDC(NULL,hdc);
        return NULL;
    }

    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hdib);

    *lpbi = bi;

    /*  call GetDIBits with a NULL lpBits param, so it will calculate the
     *  biSizeImage field for us
     */
    GetDIBits(hdc, hbm, 0L, (DWORD)bi.biHeight,
        (LPBYTE)NULL, (LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS);

    bi = *lpbi;
    GlobalUnlock(hdib);

    /* If the driver did not fill in the biSizeImage field, make one up */
    if (bi.biSizeImage == 0)
	{
        bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight;

        if (biCompression != BI_RGB)
            bi.biSizeImage = (bi.biSizeImage * 3) / 2;
    }

    /*  realloc the buffer big enough to hold all the bits */
    dwLen = bi.biSize + PaletteSize((LPBYTE)&bi) + bi.biSizeImage;
    if (h = GlobalReAlloc(hdib,dwLen,0))
        hdib = h;
    else
	{
        GlobalFree(hdib);
        hdib = NULL;

        SelectPalette(hdc,hpalT,FALSE);
        ReleaseDC(NULL,hdc);
        return hdib;
    }

    /*  call GetDIBits with a NON-NULL lpBits param, and actualy get the
     *  bits this time
     */
    lpbi = (LPBITMAPINFOHEADER)GlobalLock(hdib);

    if (! GetDIBits(hdc,
                    hbm,
                    0L,
                    (DWORD)bi.biHeight,
                    (LPBYTE)lpbi + (WORD)lpbi->biSize + PaletteSize((LPBYTE)lpbi),
                    (LPBITMAPINFO)lpbi, 
					(DWORD)DIB_RGB_COLORS))
	{
         GlobalUnlock(hdib);
         hdib = NULL;
         SelectPalette(hdc,hpalT,FALSE);
         ReleaseDC(NULL,hdc);
         return NULL;
    }
    bi = *lpbi;
   
	GlobalUnlock(hdib);
    SelectPalette(hdc,hpalT,FALSE);
    ReleaseDC(NULL,hdc);
    return hdib;
}


/************************************************************************* 
 * 
 * ChangeDIBFormat() 
 * 
 * Parameter: 
 * 
 * HDIB             - handle to packed-DIB in memory 
 * 
 * WORD             - desired bits per pixel 
 * 
 * DWORD            - desired compression format 
 * 
 * Return Value: 
 * 
 * HDIB             - handle to the new DIB if successful, else NULL 
 * 
 * Description: 
 * 
 * This function will convert the bits per pixel and/or the compression 
 * format of the specified DIB. Note: If the conversion was unsuccessful, 
 * we return NULL. The original DIB is left alone. Don't use code like the 
 * following: 
 * 
 *    hMyDIB = ChangeDIBFormat(hMyDIB, 8, BI_RLE4); 
 * 
 * The conversion will fail, but hMyDIB will now be NULL and the original 
 * DIB will now hang around in memory. We could have returned the old 
 * DIB, but we wanted to allow the programmer to check whether this 
 * conversion succeeded or failed. 
 * 
 ************************************************************************/ 
HDIB ChangeDIBFormat(HDIB hDIB, WORD wBitCount, DWORD dwCompression) 
{ 
    HBITMAP            hBitmap;         // Handle to bitmap 
    HDIB               hNewDIB = NULL;  // Handle to new DIB 
    HPALETTE           hPal;			// Handle to palette

    // Check for a valid DIB handle 
 
    if (!hDIB) 
        return NULL; 
 
    // Save the old DIB's palette 
 
    hPal = CreateDIBPalette(hDIB); 
    if (hPal == NULL)
        hPal = (HPALETTE)GetStockObject(DEFAULT_PALETTE);
 
    // Convert old DIB to a bitmap 
 
    hBitmap = DIBToBitmap(hDIB, hPal); 
    if (!hBitmap) 
    { 
        DeleteObject(hPal); 
        return NULL; 
    } 

	// Change bitmap format

	hNewDIB = ChangeBitmapFormat(hBitmap, wBitCount, dwCompression, hPal);
 
    // Clean up and return 
 
    DeleteObject(hBitmap); 
    DeleteObject(hPal); 
 
    return hNewDIB; 
} 
 

/**************************************************************************** 
* 
*     FUNCTION: ConvertDIBFormat 
* 
*     PURPOSE:  Creates a new DIB of the requested format, copies the source 
*               image to the new DIB. 
* 
*     PARAMS:   LPBITMAPINFO lpSrcDIB - the source CF_DIB 
*               UINT         nWidth   - width for new DIB 
*               UINT         nHeight  - height for new DIB 
*               UINT         nbpp     - bpp for new DIB 
*               BOOL         bStretch - TRUE to stretch source to dest 
*                                       FALSE to take upper left of image 
*				HPALETTE	 hPalSrc  - Palette used to set new DIB
*				DWORD		 dwSize   - return the size of new DIB pointer
* 
*     RETURNS:  HDIB - Handle of CF_DIB memory block with new image 
*               NULL on failure 
* 
\****************************************************************************/ 
HDIB ConvertDIBFormat(LPBYTE lpDIB, UINT nWidth, UINT nHeight, UINT nbpp, BOOL bStretch, HPALETTE hPalSrc) 
{ 
	LPBITMAPINFO lpSrcDIB =  (LPBITMAPINFO)lpDIB;
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits, lpResult; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize, dwColorNum; 
	HDIB		 hDib;
 
    // Allocate and fill out a BITMAPINFO struct for the new DIB 
    if (nbpp <= 8) 
		dwColorNum = 1 << nbpp;
	else
		dwColorNum = 0;
    dwTargetHeaderSize = sizeof( BITMAPINFO ) + ( dwColorNum * sizeof( RGBQUAD ) ); 
    lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); 
    lpbmi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); 
    lpbmi->bmiHeader.biWidth = nWidth; 
    lpbmi->bmiHeader.biHeight = nHeight; 
    lpbmi->bmiHeader.biPlanes = 1; 
    lpbmi->bmiHeader.biBitCount = nbpp; 
    lpbmi->bmiHeader.biCompression = BI_RGB; 
    lpbmi->bmiHeader.biSizeImage = 0; 
    lpbmi->bmiHeader.biXPelsPerMeter = 0; 
    lpbmi->bmiHeader.biYPelsPerMeter = 0; 
    lpbmi->bmiHeader.biClrUsed = 0; 
    lpbmi->bmiHeader.biClrImportant = 0; 
    // Fill in the color table 
    if( ! CopyColorTable( lpbmi, (LPBITMAPINFO)lpSrcDIB, hPalSrc ) ) 
    { 
        free( lpbmi ); 
        return NULL; 
    } 
 
    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    lpbmi->bmiHeader.biSizeImage = dwTargetBitsSize; 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
    // Set the color tables for the DIBSections 
    if( lpSrcDIB->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hSourceDC, 0, 1 << lpSrcDIB->bmiHeader.biBitCount, lpSrcDIB->bmiColors ); 
    if( lpbmi->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hTargetDC, 0, 1 << lpbmi->bmiHeader.biBitCount, lpbmi->bmiColors ); 
 
    // If we are asking for a straight copy, do it 
    if( (lpSrcDIB->bmiHeader.biWidth==lpbmi->bmiHeader.biWidth) && (lpSrcDIB->bmiHeader.biHeight==lpbmi->bmiHeader.biHeight) ) 
    { 
        BitBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY ); 
    } 
    else 
    { 
        // else, should we stretch it? 
        if( bStretch ) 
        { 
            SetStretchBltMode( hTargetDC, COLORONCOLOR ); 
            StretchBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, lpSrcDIB->bmiHeader.biWidth, lpSrcDIB->bmiHeader.biHeight, SRCCOPY ); 
        } 
        else 
        { 
            // or just take the upper left corner of the source 
            BitBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY ); 
        } 
    } 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // Allocate enough memory for the new CF_DIB, and copy bits 
    hDib = GlobalAlloc(GHND, dwTargetHeaderSize + dwTargetBitsSize ); 
    lpResult = (LPBYTE)GlobalLock(hDib); 
    memcpy( lpResult, lpbmi, dwTargetHeaderSize ); 
    memcpy( FindDIBBits( (LPBYTE)lpResult ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
	free( lpbmi ); 
    GlobalUnlock(hDib);
 
    return hDib; 
} 
/* End ConvertDIBFormat() 1***************************************************/ 

/**************************************************************************** 
* 
*     FUNCTION: ConvertDIBFormat 
* 
*     PURPOSE:  Creates a new DIB of the requested format, copies the source 
*               image to the new DIB. 
* 
*     PARAMS:   HDIB		 hDIB     - the source CF_DIB 
*               UINT         nWidth   - width for new DIB 
*               UINT         nHeight  - height for new DIB 
*               UINT         nbpp     - bpp for new DIB 
*               BOOL         bStretch - TRUE to stretch source to dest 
*                                       FALSE to take upper left of image 
*				HPALETTE	 hPalSrc  - Palette used to set new DIB
* 
*     RETURNS:  HDIB - new CF_DIB handle
* 
\****************************************************************************/ 
HDIB ConvertDIBFormat(HDIB hDIB, UINT nWidth, UINT nHeight, UINT nbpp, BOOL bStretch, HPALETTE hPalSrc)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits, lpResult; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize, dwColorNum; 
	HDIB		 hNewDIB;
	DWORD		 dwSize;

	// Get DIB pointer
	if (! hDIB)
		return NULL;
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)GlobalLock(hDIB);
	if (! lpSrcDIB)
		return NULL;

    // Allocate and fill out a BITMAPINFO struct for the new DIB 
    if (nbpp <= 8) 
		dwColorNum = 1 << nbpp;
	else
		dwColorNum = 0;
    dwTargetHeaderSize = sizeof( BITMAPINFO ) + ( dwColorNum * sizeof( RGBQUAD ) ); 
    lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); 
    lpbmi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); 
    lpbmi->bmiHeader.biWidth = nWidth; 
    lpbmi->bmiHeader.biHeight = nHeight; 
    lpbmi->bmiHeader.biPlanes = 1; 
    lpbmi->bmiHeader.biBitCount = nbpp; 
    lpbmi->bmiHeader.biCompression = BI_RGB; 
    lpbmi->bmiHeader.biSizeImage = 0; 
    lpbmi->bmiHeader.biXPelsPerMeter = 0; 
    lpbmi->bmiHeader.biYPelsPerMeter = 0; 
    lpbmi->bmiHeader.biClrUsed = 0; 
    lpbmi->bmiHeader.biClrImportant = 0; 
    // Fill in the color table 
    if( ! CopyColorTable( lpbmi, (LPBITMAPINFO)lpSrcDIB, hPalSrc ) ) 
    { 
        free( lpbmi ); 
        return NULL; 
    } 
 
    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    lpbmi->bmiHeader.biSizeImage = dwTargetBitsSize; 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
    // Set the color tables for the DIBSections 
    if( lpSrcDIB->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hSourceDC, 0, 1 << lpSrcDIB->bmiHeader.biBitCount, lpSrcDIB->bmiColors ); 
    if( lpbmi->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hTargetDC, 0, 1 << lpbmi->bmiHeader.biBitCount, lpbmi->bmiColors ); 
 
    // If we are asking for a straight copy, do it 
    if( (lpSrcDIB->bmiHeader.biWidth==lpbmi->bmiHeader.biWidth) && (lpSrcDIB->bmiHeader.biHeight==lpbmi->bmiHeader.biHeight) ) 
    { 
        BitBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY ); 
    } 
    else 
    { 
        // else, should we stretch it? 
        if( bStretch ) 
        { 
            SetStretchBltMode( hTargetDC, COLORONCOLOR ); 
            StretchBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, lpSrcDIB->bmiHeader.biWidth, lpSrcDIB->bmiHeader.biHeight, SRCCOPY ); 
        } 
        else 
        { 
            // or just take the upper left corner of the source 
            BitBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY ); 
        } 
    } 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // Allocate enough memory for the new CF_DIB, and copy bits 
	dwSize = dwTargetHeaderSize + dwTargetBitsSize;
	hNewDIB = GlobalAlloc(GHND, dwSize);
    lpResult = (LPBYTE)GlobalLock(hNewDIB);//malloc( dwTargetHeaderSize + dwTargetBitsSize ); 
    memcpy( lpResult, lpbmi, dwTargetHeaderSize ); 
    memcpy( FindDIBBits( (LPBYTE)lpResult ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
    free( lpbmi ); 
	GlobalUnlock(hDIB);
	GlobalUnlock(hNewDIB);
 
    return hNewDIB;
}
/* End ConvertDIBFormat() 2***************************************************/ 

/**************************************************************************** 
* 
*     FUNCTION: ConvertDIBFormat 
* 
*     PURPOSE:  Creates a new DIB of the requested format, copies the source 
*               image to the new DIB. 
* 
*     PARAMS:   LPBYTE		 lpDIB    - the source CF_DIB 
*               UINT         nbpp     - bpp for new DIB 
*				HPALETTE	 hPalSrc  - Palette used to set new DIB
* 
*     RETURNS:  HDIB - new CF_DIB handle
* 
\****************************************************************************/ 
HDIB ConvertDIBFormat(LPBYTE lpDIB, UINT nbpp, HPALETTE hPalSrc)
{
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)lpDIB;
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits, lpResult; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize, dwColorNum; 
	HDIB		 hNewDIB;
	DWORD		 dwSize;
	int			 nWidth, nHeight;

	nWidth = lpSrcDIB->bmiHeader.biWidth;
	nHeight = lpSrcDIB->bmiHeader.biHeight;

    // Allocate and fill out a BITMAPINFO struct for the new DIB 
    if (nbpp <= 8) 
		dwColorNum = 1 << nbpp;
	else
		dwColorNum = 0;
    dwTargetHeaderSize = sizeof( BITMAPINFO ) + ( dwColorNum * sizeof( RGBQUAD ) ); 
    lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); 
    lpbmi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); 
    lpbmi->bmiHeader.biWidth = nWidth; 
    lpbmi->bmiHeader.biHeight = nHeight; 
    lpbmi->bmiHeader.biPlanes = 1; 
    lpbmi->bmiHeader.biBitCount = nbpp; 
    lpbmi->bmiHeader.biCompression = BI_RGB; 
    lpbmi->bmiHeader.biSizeImage = 0; 
    lpbmi->bmiHeader.biXPelsPerMeter = 0; 
    lpbmi->bmiHeader.biYPelsPerMeter = 0; 
    lpbmi->bmiHeader.biClrUsed = 0; 
    lpbmi->bmiHeader.biClrImportant = 0; 
    // Fill in the color table 
    if( ! CopyColorTable( lpbmi, (LPBITMAPINFO)lpSrcDIB, hPalSrc ) ) 
    { 
        free( lpbmi ); 
        return NULL; 
    } 
 
    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    lpbmi->bmiHeader.biSizeImage = dwTargetBitsSize; 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
    // Set the color tables for the DIBSections 
    if( lpSrcDIB->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hSourceDC, 0, 1 << lpSrcDIB->bmiHeader.biBitCount, lpSrcDIB->bmiColors ); 
    if( lpbmi->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hTargetDC, 0, 1 << lpbmi->bmiHeader.biBitCount, lpbmi->bmiColors ); 
 
    // We are asking for a straight copy, do it 
    BitBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY ); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // Allocate enough memory for the new CF_DIB, and copy bits 
	dwSize = dwTargetHeaderSize + dwTargetBitsSize;
	hNewDIB = GlobalAlloc(GHND, dwSize);
    lpResult = (LPBYTE)GlobalLock(hNewDIB);//malloc( dwTargetHeaderSize + dwTargetBitsSize ); 
    memcpy( lpResult, lpbmi, dwTargetHeaderSize ); 
    memcpy( FindDIBBits( (LPBYTE)lpResult ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
    free( lpbmi ); 
	GlobalUnlock(hNewDIB);
 
    return hNewDIB;
}
/* End ConvertDIBFormat() 3***************************************************/ 

/**************************************************************************** 
* 
*     FUNCTION: ConvertDIBFormat 
* 
*     PURPOSE:  Creates a new DIB of the requested format, copies the source 
*               image to the new DIB. 
* 
*     PARAMS:   HDIB		 hDIB     - the source CF_DIB 
*               UINT         nbpp     - bpp for new DIB 
*				HPALETTE	 hPalSrc  - Palette used to set new DIB
* 
*     RETURNS:  HDIB - new CF_DIB handle
* 
\****************************************************************************/ 
HDIB ConvertDIBFormat(HDIB hDIB, UINT nbpp, HPALETTE hPalSrc)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits, lpResult; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize, dwColorNum; 
	HANDLE		 hNewDIB;
	DWORD		 dwSize;
	int			 nWidth, nHeight;

	// Get DIB pointer
	if (! hDIB)
		return NULL;
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)GlobalLock(hDIB);
	if (! lpSrcDIB)
		return NULL;
	nWidth = lpSrcDIB->bmiHeader.biWidth;
	nHeight = lpSrcDIB->bmiHeader.biHeight;

    // Allocate and fill out a BITMAPINFO struct for the new DIB 
    if (nbpp <= 8) 
		dwColorNum = 1 << nbpp;
	else
		dwColorNum = 0;
    dwTargetHeaderSize = sizeof( BITMAPINFO ) + ( dwColorNum * sizeof( RGBQUAD ) ); 
    lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); 
    lpbmi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); 
    lpbmi->bmiHeader.biWidth = nWidth; 
    lpbmi->bmiHeader.biHeight = nHeight; 
    lpbmi->bmiHeader.biPlanes = 1; 
    lpbmi->bmiHeader.biBitCount = nbpp; 
    lpbmi->bmiHeader.biCompression = BI_RGB; 
    lpbmi->bmiHeader.biSizeImage = 0; 
    lpbmi->bmiHeader.biXPelsPerMeter = 0; 
    lpbmi->bmiHeader.biYPelsPerMeter = 0; 
    lpbmi->bmiHeader.biClrUsed = 0; 
    lpbmi->bmiHeader.biClrImportant = 0; 
    // Fill in the color table 
    if( ! CopyColorTable( lpbmi, (LPBITMAPINFO)lpSrcDIB, hPalSrc ) ) 
    { 
        free( lpbmi ); 
        return NULL; 
    } 
 
    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    lpbmi->bmiHeader.biSizeImage = dwTargetBitsSize; 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
    // Set the color tables for the DIBSections 
    if( lpSrcDIB->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hSourceDC, 0, 1 << lpSrcDIB->bmiHeader.biBitCount, lpSrcDIB->bmiColors ); 
    if( lpbmi->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hTargetDC, 0, 1 << lpbmi->bmiHeader.biBitCount, lpbmi->bmiColors ); 
 
    // We are asking for a straight copy, do it 
    BitBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, SRCCOPY ); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // Allocate enough memory for the new CF_DIB, and copy bits 
	dwSize = dwTargetHeaderSize + dwTargetBitsSize;
	hNewDIB = GlobalAlloc(GHND, dwSize);
    lpResult = (LPBYTE)GlobalLock(hNewDIB);//malloc( dwTargetHeaderSize + dwTargetBitsSize ); 
    memcpy( lpResult, lpbmi, dwTargetHeaderSize ); 
    memcpy( FindDIBBits( (LPBYTE)lpResult ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
    free( lpbmi ); 
	GlobalUnlock(hDIB);
	GlobalUnlock(hNewDIB);
 
    return hNewDIB;
}
/* End ConvertDIBFormat() 4***************************************************/ 
 
/**************************************************************************** 
* 
*     FUNCTION: CopyColorTable 
* 
*     PURPOSE:  Copies the color table from one CF_DIB to another. 
* 
*     PARAMS:   LPBITMAPINFO lpTarget - pointer to target DIB 
*               LPBITMAPINFO lpSource - pointer to source DIB 
* 
*     RETURNS:  BOOL - TRUE for success, FALSE for failure 
* 
\****************************************************************************/ 
BOOL CopyColorTable( LPBITMAPINFO lpTarget, LPBITMAPINFO lpSource, HPALETTE hPalSrc ) 
{ 
    // What we do depends on the target's color depth 
    switch( lpTarget->bmiHeader.biBitCount ) 
    { 
        // 8bpp - need 256 entry color table 
        case 8: 
			if (hPalSrc)
			{ // Palette is provided, use it
				PALETTEENTRY    pe[256]; 
				UINT            i; 

				GetPaletteEntries( hPalSrc, 0, 256, pe ); 
				for(i=0;i<256;i++) 
				{ 
					lpTarget->bmiColors[i].rgbRed = pe[i].peRed; 
					lpTarget->bmiColors[i].rgbGreen = pe[i].peGreen; 
					lpTarget->bmiColors[i].rgbBlue = pe[i].peBlue; 
					lpTarget->bmiColors[i].rgbReserved = 0; 
				} 
			}
			else
			{ // no palette povided
				if( lpSource->bmiHeader.biBitCount == 8 ) 
				{ // Source is 8bpp too, copy color table 
					memcpy( lpTarget->bmiColors, lpSource->bmiColors, 256*sizeof(RGBQUAD) ); 
				} 
				else 
				{ // Source is != 8bpp, use Octree algorithm to create palette
					HPALETTE        hPal; 
					HDC            hDC = GetDC( NULL ); 
					PALETTEENTRY    pe[256]; 
					UINT            i; 
 
					hPal = CreateOctreePalette((LPBYTE)lpSource, 236, 8);
					if (! hPal)	 // use halftone palette                 
						hPal = CreateHalftonePalette( hDC ); 
					ReleaseDC( NULL, hDC ); 

					GetPaletteEntries( hPal, 0, 256, pe ); 
					DeleteObject( hPal ); 
					for(i=0;i<256;i++) 
					{ 
						lpTarget->bmiColors[i].rgbRed = pe[i].peRed; 
						lpTarget->bmiColors[i].rgbGreen = pe[i].peGreen; 
						lpTarget->bmiColors[i].rgbBlue = pe[i].peBlue; 
						lpTarget->bmiColors[i].rgbReserved = pe[i].peFlags; 
					} 
				}
			}
			break; // end 8bpp 
 
        // 4bpp - need 16 entry color table 
        case 4: 
			if (hPalSrc)
			{ // Palette is provided, use it
				PALETTEENTRY    pe[16]; 
				UINT            i; 

				GetPaletteEntries( hPalSrc, 0, 16, pe ); 
				for(i=0;i<16;i++) 
				{ 
					lpTarget->bmiColors[i].rgbRed = pe[i].peRed; 
					lpTarget->bmiColors[i].rgbGreen = pe[i].peGreen; 
					lpTarget->bmiColors[i].rgbBlue = pe[i].peBlue; 
					lpTarget->bmiColors[i].rgbReserved = 0; 
				} 
			}
			else
			{ // No palette is provided
				if( lpSource->bmiHeader.biBitCount == 4 ) 
				{ // Source is 4bpp too, copy color table 
					memcpy( lpTarget->bmiColors, lpSource->bmiColors, 16*sizeof(RGBQUAD) ); 
				} 
				else 
				{ // Source is != 4bpp, use system palette 
					HPALETTE        hPal; 
					PALETTEENTRY    pe[256]; 
					UINT            i; 
 
					hPal = (HPALETTE)GetStockObject( DEFAULT_PALETTE ); 
					GetPaletteEntries( hPal, 0, 16, pe ); 
					for(i=0;i<16;i++) 
					{ 
						lpTarget->bmiColors[i].rgbRed = pe[i].peRed; 
						lpTarget->bmiColors[i].rgbGreen = pe[i].peGreen; 
						lpTarget->bmiColors[i].rgbBlue = pe[i].peBlue; 
						lpTarget->bmiColors[i].rgbReserved = pe[i].peFlags; 
					}
                } 
			}
			break; // end 4bpp 
 
        // 1bpp - need 2 entry mono color table 
        case 1: 
            lpTarget->bmiColors[0].rgbRed = 0; 
            lpTarget->bmiColors[0].rgbGreen = 0; 
            lpTarget->bmiColors[0].rgbBlue = 0; 
            lpTarget->bmiColors[0].rgbReserved = 0; 
            lpTarget->bmiColors[1].rgbRed = 255; 
            lpTarget->bmiColors[1].rgbGreen = 255; 
            lpTarget->bmiColors[1].rgbBlue = 255; 
            lpTarget->bmiColors[1].rgbReserved = 0; 
			break; // end 1bpp 
 
        // no color table for the > 8bpp modes 
        case 32: 
        case 24: 
        case 16: 
        default: 
	        break; 
    } 
    return TRUE; 
} 
/* End CopyColorTable() *****************************************************/ 

/**************************************************************************** 
* 
*     FUNCTION: DIBToDIBSection 
* 
*     PURPOSE:  Create DIBSECTION from DIB
* 
*     PARAMS:   LPBYTE lpDIB - pointer to DIB data buffer
* 
*     RETURNS:  HBITMAP - handle of DIBSECTION, or NULL for failure 
* 
\****************************************************************************/ 
HBITMAP DIBToDIBSection(LPBYTE lpDIB) 
{
    LPBYTE       lpSourceBits; 
    HDC			 hDC = NULL, hSourceDC; 
    HBITMAP      hSourceBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize; 

	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)lpDIB;
	if (! lpSrcDIB)
		return NULL;

    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
 
    // Set the color tables for the DIBSections 
    if( lpSrcDIB->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hSourceDC, 0, 1 << lpSrcDIB->bmiHeader.biBitCount, lpSrcDIB->bmiColors ); 

    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    DeleteDC( hSourceDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    return hSourceBitmap;
} 

// Create DIBSECTION from DIB
HBITMAP DIBToDIBSection(HDIB hDIB) 
{
    HBITMAP      hSourceBitmap; 

	// Get DIB pointer
	if (! hDIB)
		return NULL;
	LPBYTE lpSrcDIB = (LPBYTE)GlobalLock(hDIB);
	if (! lpSrcDIB)
		return NULL;

	hSourceBitmap = DIBToDIBSection(lpSrcDIB);

    // final cleanup 
	GlobalUnlock(hDIB);
 
    return hSourceBitmap;
} 

/**************************************************************************** 
* 
*     FUNCTION: DIBSectionToDIB 
* 
*     PURPOSE:  Create DIB from DIBSECTION
* 
*     PARAMS:   HBITMAP - handle of DIBSECTION, or NULL for failure 
* 
*     RETURNS:  HBITMAP hBitmap - DIB handle
* 
\****************************************************************************/ 
HDIB DIBSectionToDIB(HBITMAP hBitmap)
{
    HDC			 hDC = NULL, hSourceDC; 
	HBITMAP		 hOldSourceBitmap;
	HANDLE		 hNewDIB;
    LPBITMAPINFO lpbmi = NULL; 
	DWORD		 dwSize;
	DIBSECTION   ds;
    DWORD		 dwColorNum; 

	// get DIB info
	GetObject(hBitmap, sizeof(DIBSECTION), &ds);
    dwColorNum = ds.dsBmih.biClrUsed; 
    if (dwColorNum == 0 && ds.dsBmih.biBitCount <= 8) 
		dwColorNum = 1 << ds.dsBmih.biBitCount;

    // Allocate and fill out a BITMAPINFO struct for the new DIB 
	dwSize = sizeof(BITMAPINFOHEADER) + (dwColorNum * sizeof(RGBQUAD)) + ds.dsBmih.biSizeImage;
	hNewDIB = GlobalAlloc(GHND, dwSize);
	if (! hNewDIB)
		return NULL;
	lpbmi = (LPBITMAPINFO)GlobalLock(hNewDIB);
	if (! lpbmi)
		return NULL;
    lpbmi->bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); 
    lpbmi->bmiHeader.biWidth = ds.dsBm.bmWidth; 
    lpbmi->bmiHeader.biHeight = ds.dsBm.bmHeight; 
    lpbmi->bmiHeader.biPlanes = 1; 
    lpbmi->bmiHeader.biBitCount = ds.dsBmih.biBitCount; 
    lpbmi->bmiHeader.biCompression = ds.dsBmih.biCompression; 
    lpbmi->bmiHeader.biSizeImage = ds.dsBmih.biSizeImage; 
    lpbmi->bmiHeader.biXPelsPerMeter = ds.dsBmih.biXPelsPerMeter; 
    lpbmi->bmiHeader.biYPelsPerMeter = ds.dsBmih.biYPelsPerMeter; 
    lpbmi->bmiHeader.biClrUsed = ds.dsBmih.biClrUsed; 
    lpbmi->bmiHeader.biClrImportant = ds.dsBmih.biClrImportant; 

	// get DC for operation
	hDC = GetDC( NULL ); 
 
	// get DIB bits
    if (! GetDIBits(hDC,
                    hBitmap,
                    0L,
                    (DWORD)ds.dsBm.bmHeight,
                    (LPBYTE)lpbmi + (WORD)lpbmi->bmiHeader.biSize + (dwColorNum*sizeof(RGBQUAD)),
                    (LPBITMAPINFO)lpbmi, 
					(DWORD)DIB_RGB_COLORS))
	{
         GlobalUnlock(hNewDIB);
         ReleaseDC(NULL,hDC);
         return NULL;
    }

	// memory DC
    hSourceDC = CreateCompatibleDC( hDC ); 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hBitmap ); 
 
    // Fill in the color table from DIBSection
    if( lpbmi->bmiHeader.biBitCount <= 8 ) 
        GetDIBColorTable( hSourceDC, 0, 1 << lpbmi->bmiHeader.biBitCount, lpbmi->bmiColors ); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    DeleteDC( hSourceDC ); 
    ReleaseDC( NULL, hDC ); 
	GlobalUnlock(hNewDIB);
 
    return hNewDIB; 
}


/************************************************************************* 
 * 
 * CopyScreenToBitmap() 
 * 
 * Parameter: 
 * 
 * LPRECT lpRect    - specifies the window 
 * 
 * Return Value: 
 * 
 * HDIB             - identifies the device-dependent bitmap 
 * 
 * Description: 
 * 
 * This function copies the specified part of the screen to a device- 
 * dependent bitmap. 
 * 
 * 
 ************************************************************************/ 
HBITMAP CopyScreenToBitmap(LPRECT lpRect) 
{ 
    HDC         hScrDC, hMemDC;         // screen DC and memory DC 
    HBITMAP     hBitmap, hOldBitmap;    // handles to deice-dependent bitmaps 
    int         nX, nY, nX2, nY2;       // coordinates of rectangle to grab 
    int         nWidth, nHeight;        // DIB width and height 
    int         xScrn, yScrn;           // screen resolution 
 
    // check for an empty rectangle 
 
    if (IsRectEmpty(lpRect)) 
      return NULL; 
 
    // create a DC for the screen and create 
    // a memory DC compatible to screen DC 
     
    hScrDC = CreateDC("DISPLAY", NULL, NULL, NULL); 
    hMemDC = CreateCompatibleDC(hScrDC); 
 
    // get points of rectangle to grab 
 
    nX = lpRect->left; 
    nY = lpRect->top; 
    nX2 = lpRect->right; 
    nY2 = lpRect->bottom; 
 
    // get screen resolution 
 
    xScrn = GetDeviceCaps(hScrDC, HORZRES); 
    yScrn = GetDeviceCaps(hScrDC, VERTRES); 
 
    //make sure bitmap rectangle is visible 
 
    if (nX < 0) 
        nX = 0; 
    if (nY < 0) 
        nY = 0; 
    if (nX2 > xScrn) 
        nX2 = xScrn; 
    if (nY2 > yScrn) 
        nY2 = yScrn; 
 
    nWidth = nX2 - nX; 
    nHeight = nY2 - nY; 
 
    // create a bitmap compatible with the screen DC 
    hBitmap = CreateCompatibleBitmap(hScrDC, nWidth, nHeight); 
 
    // select new bitmap into memory DC 
    hOldBitmap = (HBITMAP)SelectObject(hMemDC, hBitmap); 
 
    // bitblt screen DC to memory DC 
    BitBlt(hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, SRCCOPY); 
 
    // select old bitmap back into memory DC and get handle to 
    // bitmap of the screen 
     
    hBitmap = (HBITMAP)SelectObject(hMemDC, hOldBitmap); 
 
    // clean up 
 
    DeleteDC(hScrDC); 
    DeleteDC(hMemDC); 
 
    // return handle to the bitmap 
 
    return hBitmap; 
} 

/************************************************************************* 
 * 
 * CopyWindowToBitmap() 
 * 
 * Parameters: 
 * 
 * HWND hWnd        - specifies the window 
 * 
 * WORD fPrintArea  - specifies the window area to copy into the device- 
 *                    dependent bitmap 
 * 
 * Return Value: 
 * 
 * HDIB         - identifies the device-dependent bitmap 
 * 
 * Description: 
 * 
 * This function copies the specified part(s) of the window to a device- 
 * dependent bitmap. 
 * 
 * 
 ************************************************************************/ 
HBITMAP CopyWindowToBitmap(HWND hWnd, WORD fPrintArea) 
{ 
    HBITMAP     hBitmap = NULL;  // handle to device-dependent bitmap 
 
    // check for a valid window handle 
 
    if (!hWnd) 
        return NULL; 
 
    switch (fPrintArea) 
    { 
        case PW_WINDOW: // copy entire window 
        { 
            RECT    rectWnd; 
 
            // get the window rectangle 
 
            GetWindowRect(hWnd, &rectWnd); 
 
            // get the bitmap of that window by calling 
            // CopyScreenToBitmap and passing it the window rect 
        
            hBitmap = CopyScreenToBitmap(&rectWnd); 
            break; 
        } 
         
 
        case PW_CLIENT: // copy client area 
        { 
            RECT rectClient; 
            POINT pt1, pt2; 
 
            // get client dimensions 
 
            GetClientRect(hWnd, &rectClient); 
 
            // convert client coords to screen coords 
 
            pt1.x = rectClient.left; 
            pt1.y = rectClient.top; 
            pt2.x = rectClient.right; 
            pt2.y = rectClient.bottom; 
            ClientToScreen(hWnd, &pt1); 
            ClientToScreen(hWnd, &pt2); 
            rectClient.left = pt1.x; 
            rectClient.top = pt1.y; 
            rectClient.right = pt2.x; 
            rectClient.bottom = pt2.y; 
 
            // get the bitmap of the client area by calling 
            // CopyScreenToBitmap and passing it the client rect 
             
            hBitmap = CopyScreenToBitmap(&rectClient); 
            break; 
        } 
     
        default:    // invalid print area 
            return NULL; 
    } 
 
    // return handle to the bitmap 
 
   return hBitmap; 
} 
 
 
/************************************************************************* 
 * 
 * CopyClientRectToBitmap() 
 * 
 * Parameters: 
 * 
 * HWND hWnd        - specifies the window 
 * 
 * LPRECT lpRect    - specifies the client area rect to copy into the device- 
 *                    independent bitmap 
 * 
 * Return Value: 
 * 
 * HDIB             - identifies the device-independent bitmap 
 * 
 * Description: 
 * 
 * This function copies the specified part(s) of the window client to a 
 * device-independent bitmap. 
 * 
 ************************************************************************/ 
HBITMAP CopyClientRectToBitmap(HWND hWnd, LPRECT lpRect) 
{ 
   HBITMAP     hBitmap = NULL;  // handle to DIB 
 
   // check for a valid window handle 
 
    if (!hWnd) 
        return NULL; 
 
    POINT   pt1, pt2; 

    // convert client coords to screen coords 

    pt1.x = lpRect->left; 
    pt1.y = lpRect->top; 
    pt2.x = lpRect->right; 
    pt2.y = lpRect->bottom; 
    ClientToScreen(hWnd, &pt1); 
    ClientToScreen(hWnd, &pt2); 
    lpRect->left = pt1.x; 
    lpRect->top = pt1.y; 
    lpRect->right = pt2.x; 
    lpRect->bottom = pt2.y; 

    // get the DIB of the client area by calling 
    // CopyScreenToDIB and passing it the client rect 

    hBitmap = CopyScreenToBitmap(lpRect); 
 
	// restore lpRect

    pt1.x = lpRect->left; 
    pt1.y = lpRect->top; 
    pt2.x = lpRect->right; 
    pt2.y = lpRect->bottom; 
    ScreenToClient(hWnd, &pt1); 
    ScreenToClient(hWnd, &pt2); 
    lpRect->left = pt1.x; 
    lpRect->top = pt1.y; 
    lpRect->right = pt2.x; 
    lpRect->bottom = pt2.y; 

	// return the handle to the DIB 
 
   return hBitmap; 
} 
 
/************************************************************************* 
 * 
 * CopyScreenToDIB() 
 * 
 * Parameter: 
 * 
 * LPRECT lpRect    - specifies the window 
 * 
 * Return Value: 
 * 
 * HDIB             - identifies the device-independent bitmap 
 * 
 * Description: 
 * 
 * This function copies the specified part of the screen to a device- 
 * independent bitmap. 
 * 
 ************************************************************************/ 
HDIB CopyScreenToDIB(LPRECT lpRect) 
{ 
    HBITMAP     hBitmap;        // handle to device-dependent bitmap 
    HPALETTE    hPalette;       // handle to palette 
    HDIB        hDIB = NULL;    // handle to DIB 
 
    // get the device-dependent bitmap in lpRect by calling 
    //  CopyScreenToBitmap and passing it the rectangle to grab 
 
    hBitmap = CopyScreenToBitmap(lpRect); 
 
    // check for a valid bitmap handle 
 
    if (!hBitmap) 
      return NULL; 
 
    // get the current palette 
 
    hPalette = GetSystemPalette(); 
 
    // convert the bitmap to a DIB 
 
    hDIB = BitmapToDIB(hBitmap, hPalette); 
 
    // clean up  
 
    DeleteObject(hPalette); 
    DeleteObject(hBitmap); 
 
    // return handle to the packed-DIB 
    return hDIB; 
} 
 
/************************************************************************* 
 * 
 * CopyWindowToDIB() 
 * 
 * Parameters: 
 * 
 * HWND hWnd        - specifies the window 
 * 
 * WORD fPrintArea  - specifies the window area to copy into the device- 
 *                    independent bitmap 
 * 
 * Return Value: 
 * 
 * HDIB             - identifies the device-independent bitmap 
 * 
 * Description: 
 * 
 * This function copies the specified part(s) of the window to a device- 
 * independent bitmap. 
 * 
 ************************************************************************/ 
HDIB CopyWindowToDIB(HWND hWnd, WORD fPrintArea) 
{ 
   HDIB     hDIB = NULL;  // handle to DIB 
 
   // check for a valid window handle 
 
    if (!hWnd) 
        return NULL; 
 
    switch (fPrintArea) 
    { 
        case PW_WINDOW: // copy entire window 
        { 
            RECT    rectWnd; 
 
            // get the window rectangle 
 
            GetWindowRect(hWnd, &rectWnd); 
 
            // get the DIB of the window by calling 
            // CopyScreenToDIB and passing it the window rect 
             
            hDIB = CopyScreenToDIB(&rectWnd); 
            break; 
        } 
       
        case PW_CLIENT: // copy client area 
        { 
            RECT    rectClient; 
            POINT   pt1, pt2; 
 
            // get the client area dimensions 
 
            GetClientRect(hWnd, &rectClient); 
 
            // convert client coords to screen coords 
 
            pt1.x = rectClient.left; 
            pt1.y = rectClient.top; 
            pt2.x = rectClient.right; 
            pt2.y = rectClient.bottom; 
            ClientToScreen(hWnd, &pt1); 
            ClientToScreen(hWnd, &pt2); 
            rectClient.left = pt1.x; 
            rectClient.top = pt1.y; 
            rectClient.right = pt2.x; 
            rectClient.bottom = pt2.y; 
 
            // get the DIB of the client area by calling 
            // CopyScreenToDIB and passing it the client rect 
 
            hDIB = CopyScreenToDIB(&rectClient); 
            break; 
        } 
       
        default:    // invalid print area 
            return NULL; 
    } 
 
   // return the handle to the DIB 
 
   return hDIB; 
} 
 
/************************************************************************* 
 * 
 * CopyClientRectToDIB() 
 * 
 * Parameters: 
 * 
 * HWND hWnd        - specifies the window 
 * 
 * LPRECT lpRect    - specifies the client area rect to copy into the device- 
 *                    independent bitmap 
 * 
 * Return Value: 
 * 
 * HDIB             - identifies the device-independent bitmap 
 * 
 * Description: 
 * 
 * This function copies the specified part(s) of the window client to a 
 * device-independent bitmap. 
 * 
 ************************************************************************/ 
HDIB CopyClientRectToDIB(HWND hWnd, LPRECT lpRect) 
{ 
   HDIB     hDIB = NULL;  // handle to DIB 
 
   // check for a valid window handle 
 
    if (!hWnd) 
        return NULL; 
 
    POINT   pt1, pt2; 

    // convert client coords to screen coords 

    pt1.x = lpRect->left; 
    pt1.y = lpRect->top; 
    pt2.x = lpRect->right; 
    pt2.y = lpRect->bottom; 
    ClientToScreen(hWnd, &pt1); 
    ClientToScreen(hWnd, &pt2); 
    lpRect->left = pt1.x; 
    lpRect->top = pt1.y; 
    lpRect->right = pt2.x; 
    lpRect->bottom = pt2.y; 

    // get the DIB of the client area by calling 
    // CopyScreenToDIB and passing it the client rect 

    hDIB = CopyScreenToDIB(lpRect); 
 
	// restore lpRect

    pt1.x = lpRect->left; 
    pt1.y = lpRect->top; 
    pt2.x = lpRect->right; 
    pt2.y = lpRect->bottom; 
    ScreenToClient(hWnd, &pt1); 
    ScreenToClient(hWnd, &pt2); 
    lpRect->left = pt1.x; 
    lpRect->top = pt1.y; 
    lpRect->right = pt2.x; 
    lpRect->bottom = pt2.y; 

	// return the handle to the DIB 
 
   return hDIB; 
} 

 
/************************************************************************* 
 * 
 * PaintDIB() 
 * 
 * Parameters: 
 * 
 * HDC hDC          - DC to do output to 
 * 
 * LPRECT lpDCRect  - rectangle on DC to do output to 
 * 
 * HDIB hDIB        - handle to global memory with a DIB spec 
 *                    in it followed by the DIB bits 
 * 
 * LPRECT lpDIBRect - rectangle of DIB to output into lpDCRect 
 *
 * HPALETTE hPal    - Palette used to diaplsy DIB, if is NULL, 
 *					  use DIB palette to display
 *
 * DWORD dwRop      - ROP mode to display DIB
 * 
 * Return Value: 
 * 
 * BOOL             - TRUE if DIB was drawn, FALSE otherwise 
 * 
 * Description: 
 *   Painting routine for a DIB.  Calls StretchDIBits() or 
 *   SetDIBitsToDevice() to paint the DIB.  The DIB is 
 *   output to the specified DC, at the coordinates given 
 *   in lpDCRect.  The area of the DIB to be output is 
 *   given by lpDIBRect. 
 * 
 * NOTE: This function always selects the palette as background. Before 
 * calling this function, be sure your palette is selected to desired 
 * priority (foreground or background). 
 * 
 * 
 ************************************************************************/ 
BOOL PaintDIB(HDC hDC, LPRECT lpDCRect, HDIB hDIB, LPRECT lpDIBRect, 
        HPALETTE hPal, DWORD dwRop)
{ 
    LPBYTE        lpDIBHdr;           // Pointer to BITMAPINFOHEADER 
    LPBYTE        lpDIBBits;          // Pointer to DIB bits 
    BOOL         bSuccess=FALSE;     // Success/fail flag 
    HPALETTE     hOldPal=NULL;       // Previous palette 
 
    // Check for valid DIB handle 
 
    if (!hDIB) 
        return FALSE; 

	// get DIB bitcount and device bits/pixel

	int nDeviceBitsPixel = GetDeviceCaps(hDC, BITSPIXEL);
	int nDIBBitsPixel = DIBBitCount(hDIB);

	// if device can not display DIB color, dithering DIB
	
	if (nDIBBitsPixel > 8 && nDeviceBitsPixel <= 8)
		return DitherDisplayDIB(hDC, lpDCRect, hDIB, lpDIBRect, dwRop);
 
    // Lock down the DIB, and get a pointer to the beginning of the bit 
    // buffer 
     
    lpDIBHdr  = (LPBYTE)GlobalLock(hDIB); 
    lpDIBBits = FindDIBBits(lpDIBHdr); 
 
	// if no palette provided, create one from DIB

    if (! hPal) 
		hPal = CreateDIBPalette(lpDIBHdr);

    // Select and realize our palette as background 
 
    if (hPal) 
    { 
        hOldPal = SelectPalette(hDC, hPal, TRUE); 
        RealizePalette(hDC); 
    } 
 
    // Make sure to use the stretching mode best for color pictures 
 
    SetStretchBltMode(hDC, COLORONCOLOR); 
 
    // Call StretchDIBits() with dwRop
 
	bSuccess = StretchDIBits(hDC, 
							 lpDCRect->left, 
							 lpDCRect->top, 
							 RECTWIDTH(lpDCRect), 
							 RECTHEIGHT(lpDCRect), 
							 lpDIBRect->left, 
				             (int)DIBHeight(lpDIBHdr) - lpDIBRect->top - RECTHEIGHT(lpDIBRect), 
							 RECTWIDTH(lpDIBRect), 
							 RECTHEIGHT(lpDIBRect), 
							 lpDIBBits, 
							 (LPBITMAPINFO)lpDIBHdr, 
							 DIB_RGB_COLORS, 
							 SRCCOPY); 
 
    // Unlock the memory block 
 
    GlobalUnlock(hDIB); 
 
    // Reselect old palette 
 
    if (hOldPal) 
		SelectPalette(hDC, hOldPal, FALSE); 
 
    // Return with success/fail flag 
    return bSuccess; 
} 

/************************************************************************* 
 * 
 * DitherDisplayDIB() 
 * 
 * Parameters: 
 * 
 * HDC hDC          - DC to do output to 
 * 
 * LPRECT lpDCRect  - rectangle on DC to do output to 
 * 
 * HDIB hDIB        - handle to global memory with a DIB spec 
 *                    in it followed by the DIB bits 
 * 
 * LPRECT lpDIBRect - rectangle of DIB to output into lpDCRect 
 *
 * HPALETTE hPal    - Palette used to diaplsy DIB, if is NULL, 
 *					  use DIB palette to display
 *
 * DWORD dwRop      - ROP mode to display DIB
 * 
 * Return Value: 
 * 
 * BOOL             - TRUE if DIB was drawn, FALSE otherwise 
 * 
 * Description: 
 *   Painting routine with dithering, can display accuraly a DIB with
 *   more colors than the display device can support.
 * 
 ************************************************************************/ 
BOOL DitherDisplayDIB(HDC hDC, LPRECT lpDCRect, HDIB hDIB, LPRECT lpDIBRect, 
        DWORD dwRop)
{ 
    LPBYTE        lpDIBHdr;           // Pointer to BITMAPINFOHEADER 
    LPBYTE        lpDIBBits;          // Pointer to DIB bits 
    BOOL          bSuccess=FALSE;     // Success/fail flag 
    HPALETTE      hPal=NULL;		  // used palette 
    HPALETTE      hOldPal=NULL;       // Previous palette 
 
    // Check for valid DIB handle 
 
    if (!hDIB) 
        return FALSE; 

	// create dithered DIB to display
	
	HDIB hDIBDisplay = CreateDither8BppDIB(hDIB);

    // Lock down the DIB, and get a pointer to the beginning of the bit 
    // buffer 
     
    lpDIBHdr  = (LPBYTE)GlobalLock(hDIBDisplay); 
    lpDIBBits = FindDIBBits(lpDIBHdr); 
 
	// create palette from DIB

	hPal = CreateDIBPalette(lpDIBHdr);

    // Select and realize our palette as background 

    if (hPal) 
    { 
		hOldPal = SelectPalette(hDC, hPal, TRUE); 
        RealizePalette(hDC); 
    } 

    // Make sure to use the stretching mode best for color pictures 
 
    SetStretchBltMode(hDC, COLORONCOLOR); 
 
    // call StretchDIBits() anyway with dwRop
 
	bSuccess = StretchDIBits(hDC, 
							 lpDCRect->left, 
							 lpDCRect->top, 
							 RECTWIDTH(lpDCRect), 
							 RECTHEIGHT(lpDCRect), 
							 lpDIBRect->left, 
				             (int)DIBHeight(lpDIBHdr) - lpDIBRect->top - RECTHEIGHT(lpDIBRect), 
							 RECTWIDTH(lpDIBRect), 
							 RECTHEIGHT(lpDIBRect), 
							 lpDIBBits, 
							 (LPBITMAPINFO)lpDIBHdr, 
							 DIB_RGB_COLORS, 
							 SRCCOPY); 

    // Unlock and free the memory block 
 
    GlobalUnlock(hDIBDisplay); 
	GlobalFree(hDIBDisplay); 

    // Reselect old palette 
 
    if (hOldPal) 
		SelectPalette(hDC, hOldPal, FALSE); 
 
    // Return with success/fail flag 
    return bSuccess; 
} 

/************************************************************************* 
 * 
 * PaintBitmap() 
 * 
 * Parameters: 
 * 
 * HDC hDC          - DC to do output to 
 * 
 * LPRECT lpDCRect  - rectangle on DC to do output to 
 * 
 * HBITMAP hDDB     - handle to device-dependent bitmap (DDB) 
 * 
 * LPRECT lpDDBRect - rectangle of DDB to output into lpDCRect 
 * 
 * HPALETTE hPalette - handle to the palette to use with hDDB 
 * 
 * Return Value: 
 * 
 * BOOL             - TRUE if bitmap was drawn, FLASE otherwise 
 * 
 * Description: 
 * 
 * Painting routine for a DDB.  Calls BitBlt() or 
 * StretchBlt() to paint the DDB.  The DDB is 
 * output to the specified DC, at the coordinates given 
 * in lpDCRect.  The area of the DDB to be output is 
 * given by lpDDBRect.  The specified palette is used. 
 * 
 * NOTE: This function always selects the palette as background. Before 
 * calling this function, be sure your palette is selected to desired 
 * priority (foreground or background). 
 * 
 ************************************************************************/ 
BOOL PaintBitmap(HDC hDC, LPRECT lpDCRect, HBITMAP hDDB,  LPRECT lpDDBRect,  
        HPALETTE hPal, DWORD dwRop)
{ 
    HDC         hMemDC;            // Handle to memory DC 
    HBITMAP     hOldBitmap;        // Handle to previous bitmap 
    HPALETTE    hOldPal1 = NULL;   // Handle to previous palette 
    HPALETTE    hOldPal2 = NULL;   // Handle to previous palette 
    BOOL        bSuccess = FALSE;  // Success/fail flag 
 
    // Create a memory DC 
 
    hMemDC = CreateCompatibleDC (hDC); 
 
    // If this failed, return FALSE 
 
    if (!hMemDC) 
        return FALSE; 
 
    // If we have a palette, select and realize it 
 
    if (hPal) 
    { 
        hOldPal1 = SelectPalette(hMemDC, hPal, TRUE); 
        hOldPal2 = SelectPalette(hDC, hPal, TRUE); 
        RealizePalette(hDC); 
    } 
 
    // Select bitmap into the memory DC 
 
    hOldBitmap = (HBITMAP)SelectObject (hMemDC, hDDB); 
 
    // Make sure to use the stretching mode best for color pictures 
 
    SetStretchBltMode (hDC, COLORONCOLOR); 
 
    // Determine whether to call StretchBlt() or BitBlt() 
    if ((RECTWIDTH(lpDCRect)  == RECTWIDTH(lpDDBRect)) && 
            (RECTHEIGHT(lpDCRect) == RECTHEIGHT(lpDDBRect))) 
        bSuccess = BitBlt(hDC, lpDCRect->left, lpDCRect->top, 
                lpDCRect->right - lpDCRect->left, 
                lpDCRect->bottom - lpDCRect->top, hMemDC, lpDDBRect->left, 
                lpDDBRect->top, dwRop); 
    else 
        bSuccess = StretchBlt(hDC, lpDCRect->left,  lpDCRect->top,  
                lpDCRect->right - lpDCRect->left, 
                lpDCRect->bottom - lpDCRect->top, hMemDC, lpDDBRect->left,  
                lpDDBRect->top,  lpDDBRect->right - lpDDBRect->left, 
                lpDDBRect->bottom - lpDDBRect->top, dwRop); 
 
    // Clean up 
 
    SelectObject(hMemDC, hOldBitmap); 
 
    if (hOldPal1) 
        SelectPalette (hMemDC, hOldPal1, FALSE); 
 
    if (hOldPal2) 
        SelectPalette (hDC, hOldPal2, FALSE); 
 
    DeleteDC (hMemDC); 
 
    // Return with success/fail flag 
 
    return bSuccess; 
} 

/************************************************************************* 
 * 
 * DisplayPalette () 
 * 
 * Parameter: 
 * 
 * HDC hDC          - handle of device context to display palette
 * LPRECT lpRect      - rect range to show palette
 * HPALETTE hPal     - handle of palette to display
 * 
 * Return Value: 
 * 
 * BOOL            - TRUE, if success, else FALSE
 * 
 * Description: 
 * 
 * This function displays palette on the specified rectangle in a device context 
 * 
 ************************************************************************/ 
BOOL DisplayPalette(HDC hDC, LPRECT lpRect, HPALETTE hPal)
{
	if (! hPal)
		return FALSE;

	int nEntries;
	PALETTEENTRY pe[256];
	nEntries = GetPaletteEntries(hPal, 0, 256, pe);
	int nSqr = (int)sqrt((double)nEntries);

	int nWidth = (lpRect->right-lpRect->left)/nSqr;
	int nHeight = (lpRect->bottom-lpRect->top)/nSqr;
	lpRect->right = lpRect->left + nWidth*nSqr;
	lpRect->bottom = lpRect->top + nHeight*nSqr;

	HPALETTE hOldPal = (HPALETTE)SelectPalette(hDC, hPal, FALSE);
	RealizePalette(hDC);

	HBRUSH hBrush, hOldBrush;
	int x, y;
	for (int i=0; i<nEntries; ++i)
	{
		x = i%nSqr;
		y = i/nSqr;
		hBrush = CreateSolidBrush(RGB(pe[i].peRed, pe[i].peGreen, pe[i].peBlue));
		hOldBrush = (HBRUSH)SelectObject(hDC, hBrush);
		Rectangle(hDC, 
			      lpRect->left + x*nWidth, 
				  lpRect->top + y*nHeight, 
				  lpRect->left + (x+1)*nWidth, 
				  lpRect->top + (y+1) *nHeight);
		SelectObject(hDC, hOldBrush);
		DeleteObject(hBrush);
	}
	
	SelectPalette(hDC, hOldPal, FALSE);

	return TRUE;
}

/************************************************************************* 
 * 
 * CopyPalette () 
 * 
 * Parameter: 
 * 
 * HPALETTE hPalSrc    - source palette handle
 * 
 * Return Value: 
 * 
 * HPALETTE          - destination palette handle
 * 
 * Description: 
 * 
 * This function copys the source palette to a new palette handle 
 * 
 ************************************************************************/ 
HPALETTE CopyPalette(HPALETTE hPalSrc)
{
    PLOGPALETTE     plogPal;
    int             iNumEntries=0;
    HPALETTE        hPal;
	HANDLE			h;

	iNumEntries = GetPaletteEntries(hPalSrc, 0, iNumEntries, NULL);
    if (iNumEntries == 0) 
        return (HPALETTE) NULL;

	h = GlobalAlloc(GHND, sizeof(DWORD) + sizeof(PALETTEENTRY)*iNumEntries);
	if (! h)
        return (HPALETTE) NULL;
	plogPal = (PLOGPALETTE)GlobalLock(h);
    if (! plogPal) 
        return (HPALETTE) NULL;

    plogPal->palVersion = 0x300;
    plogPal->palNumEntries = (WORD) iNumEntries;
    GetPaletteEntries(hPalSrc, 0, iNumEntries, plogPal->palPalEntry);
    hPal = CreatePalette(plogPal);

    GlobalUnlock(h);
    GlobalFree(h);

    return hPal;
}
 
/************************************************************************* 
 * 
 * PalEntriesOnDevice() 
 * 
 * Parameter: 
 * 
 * HDC hDC          - device context 
 * 
 * Return Value: 
 * 
 * int              - number of palette entries on device 
 * 
 * Description: 
 * 
 * This function gets the number of palette entries on the specified device 
 * 
 ************************************************************************/ 
int PalEntriesOnDevice(HDC hDC) 
{ 
    int nColors;  // number of colors 
 
    // Find out the number of colors on this device. 
     
    nColors = (1 << (GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES))); 
 
    assert(nColors); 
    return nColors; 
} 
 
 
/************************************************************************* 
 * 
 * GetSystemPalette() 
 * 
 * Parameters: 
 * 
 * None 
 * 
 * Return Value: 
 * 
 * HPALETTE         - handle to a copy of the current system palette 
 * 
 * Description: 
 * 
 * This function returns a handle to a palette which represents the system 
 * palette.  The system RGB values are copied into our logical palette using 
 * the GetSystemPaletteEntries function.   
 * 
 ************************************************************************/ 
HPALETTE GetSystemPalette(void) 
{ 
    HDC hDC;                // handle to a DC 
    static HPALETTE hPal = NULL;   // handle to a palette 
    HANDLE hLogPal;         // handle to a logical palette 
    LPLOGPALETTE lpLogPal;  // pointer to a logical palette 
    int nColors;            // number of colors 
 
    // Find out how many palette entries we want. 
 
    hDC = GetDC(NULL); 
 
    if (!hDC) 
        return NULL; 
 
    nColors = PalEntriesOnDevice(hDC);   // Number of palette entries 
 
    // Allocate room for the palette and lock it. 
 
    hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + nColors * 
            sizeof(PALETTEENTRY)); 
 
    // if we didn't get a logical palette, return NULL 
 
    if (!hLogPal) 
        return NULL; 
 
    // get a pointer to the logical palette 
 
    lpLogPal = (LPLOGPALETTE)GlobalLock(hLogPal); 
 
    // set some important fields 
 
    lpLogPal->palVersion = PALVERSION; 
    lpLogPal->palNumEntries = nColors; 
 
    // Copy the current system palette into our logical palette 
 
    GetSystemPaletteEntries(hDC, 0, nColors, 
            (LPPALETTEENTRY)(lpLogPal->palPalEntry)); 
 
    // Go ahead and create the palette.  Once it's created, 
    // we no longer need the LOGPALETTE, so free it.     
 
    hPal = CreatePalette(lpLogPal); 
 
    // clean up 
 
    GlobalUnlock(hLogPal); 
    GlobalFree(hLogPal); 
    ReleaseDC(NULL, hDC); 
 
    return hPal; 
} 
 
/************************************************************************* 
 * 
 * CreateIdentifyPalette () 
 * 
 * Parameter: 
 * 
 * HPALETTE hPalSrc    - source palette handle
 * 
 * Return Value: 
 * 
 * HPALETTE          - destination identify palette handle
 * 
 * Description: 
 * 
 * This function creates an identify palette from the source palette handle 
 * 
 ************************************************************************/ 
HPALETTE CreateIdentifyPalette(HPALETTE hPalSrc)
{
    BOOL bResult = FALSE;
    int i, iSysColors, iPalEntries;
    HPALETTE hPalette, hpalOld;

	if (! hPalSrc)
		return NULL;

	// create a new palette equal to input
	hPalette = CopyPalette(hPalSrc);

    // Get a screen DC to work with
    HDC hdcScreen = GetDC(NULL);
    ASSERT(hdcScreen);

    // Make sure we are on a palettized device
    if (!(GetDeviceCaps(hdcScreen, RASTERCAPS) & RC_PALETTE)) 
	{
        TRACE("Not a palettized device");
        goto abort;
    }

    // Get the number of system colors and the number of palette entries
    // Note that on a palletized device the number of colors is the
    // number of guaranteed colors.  I.e. the number of reserved system colors
    iSysColors = GetDeviceCaps(hdcScreen, NUMCOLORS);
    iPalEntries = GetDeviceCaps(hdcScreen, SIZEPALETTE);

    // if there are more than 256 colors we are wasting our time
    if (iSysColors > 256) goto abort;

    // Now we force the palette manager to reset its tables so that
    // the next palette to be realized will get its colors in the order they are 
    // in the logical palette. This is done by changing the number of
    // reserved colors.
    SetSystemPaletteUse(hdcScreen, SYSPAL_NOSTATIC);
    SetSystemPaletteUse(hdcScreen, SYSPAL_STATIC);

    // Select our palette into the screen DC and realize it so that
    // its colors will be entered into the free slots in the physical palette
    hpalOld = ::SelectPalette(hdcScreen,
                              hPalette, // our hpal
                              FALSE);
    ::RealizePalette(hdcScreen);
    // Now replace the old palette (but don't realize it)
    ::SelectPalette(hdcScreen, hpalOld, FALSE);

    // The physical palette now has our colors set in place and its own
    // reserved colors at either end.  We can grab the lot now
    PALETTEENTRY pe[256];
    GetSystemPaletteEntries(hdcScreen, 
                            0,
                            iPalEntries,
                            pe);

    // Set the PC_NOCOLLAPSE flag for each of our colors so that GDI
    // won't merge them together.  Be careful not to set PC_NOCOLLAPSE for the 
    // sys color entries or we'll get multpile copies of these colors in
    // the palette when we realize it.
    for (i = 0; i < iSysColors/2; i++) {
        pe[i].peFlags = 0;
    }
    for (; i < iPalEntries-iSysColors/2; i++) {
        pe[i].peFlags = PC_NOCOLLAPSE;
    }
    for (; i < iPalEntries; i++) {
        pe[i].peFlags = 0;
    }

    // Resize the palette in case it was smaller
    ResizePalette(hPalette, iPalEntries);

    // Update the palette entries with what is now in the physical palette
    SetPaletteEntries(hPalette, 0, iPalEntries, pe);
    bResult = TRUE;

abort:
    ::ReleaseDC(NULL, hdcScreen);
    return bResult ? hPalette : NULL;
}

/************************************************************************* 
 * 
 * MapDIBColorsToPalette () 
 * 
 * Parameter: 
 * 
 * HDIB hDIB		    - DIB handle
 * HPALETTE hPalette    - given palette handle
 * 
 * Return Value: 
 * 
 * HPALETTE          - destination identify palette handle
 * 
 * Description: 
 * 
 * This function Map the colors in a DIB to a given palette.
 * 
 ************************************************************************/ 
BOOL MapDIBColorsToPalette(HDIB hDIB, HPALETTE hPalette)
{
    if (! hDIB)
        return FALSE;
    if (! hPalette)
        return FALSE;

    LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB);
    if (! lpbi)
        return FALSE;
    LPRGBQUAD pctThis = (LPRGBQUAD)((LPBYTE)lpbi + lpbi->biSize);

    // build an index translation table to map this DIBs colors
    // to those of the reference DIB
    BYTE imap[256];
    for (int i = 0; i < 256; i++) 
	{
        imap[i] = (BYTE)GetNearestPaletteIndex(hPalette, 
									RGB(pctThis->rgbRed,
										pctThis->rgbGreen,
										pctThis->rgbBlue));
        pctThis++;
    }
    // now map the DIB bits
    LPBYTE pBits = (LPBYTE)lpbi + lpbi->biSize + PaletteSize((LPBYTE)lpbi);
    int iSize = WIDTHBYTES(lpbi->biBitCount*lpbi->biWidth) * lpbi->biHeight;
    while (iSize--) 
	{
        *pBits = imap[*pBits];
        pBits++;
    }
    // Now reset the DIB color table so that its RGB values match
    // those in the palette
    PALETTEENTRY pe[256];
    GetPaletteEntries(hPalette, 0, 256, pe);
    pctThis = (LPRGBQUAD)((LPBYTE)lpbi + lpbi->biSize);
    for (i = 0; i < 256; i++) 
	{
        pctThis->rgbRed = pe[i].peRed;    
        pctThis->rgbGreen = pe[i].peGreen;    
        pctThis->rgbBlue = pe[i].peBlue;
        pctThis++;    
    }
	
	GlobalUnlock(hDIB);
    return TRUE;
}

/************************************************************************* 
 * 
 * DrawTransparentBitmap () 
 * 
 * Parameters: 
 * 
 * HDC hDC          - DC to do output to 
 * 
 * HBITMAP hBitmap    - handle of bitmap to draw transparently
 * 
 * LONG xStart, yStart   - start position to draw *
 *
 * COLORREF cTransparentColor - transparent color * 
 * Return Value: 
 * 
 * Description: none
 *		 Draw bitmap transparently
 * 
 ************************************************************************/ 
void DrawTransparentBitmap(HDC hdc, HBITMAP hBitmap, 
						   LONG xStart, LONG yStart, 
						   COLORREF cTransparentColor)
{
   BITMAP     bm;
   COLORREF   cColor;
   HBITMAP    bmAndBack, bmAndObject, bmAndMem, bmSave;
   HBITMAP    bmBackOld, bmObjectOld, bmMemOld, bmSaveOld;
   HDC        hdcMem, hdcBack, hdcObject, hdcTemp, hdcSave;
   POINT      ptSize;

   hdcTemp = CreateCompatibleDC(hdc);
   SelectObject(hdcTemp, hBitmap);   // Select the bitmap

   GetObject(hBitmap, sizeof(BITMAP), (LPBYTE)&bm);
   ptSize.x = bm.bmWidth;            // Get width of bitmap
   ptSize.y = bm.bmHeight;           // Get height of bitmap
   DPtoLP(hdcTemp, &ptSize, 1);      // Convert from device
                                     // to logical points

   // Create some DCs to hold temporary data.
   hdcBack   = CreateCompatibleDC(hdc);
   hdcObject = CreateCompatibleDC(hdc);
   hdcMem    = CreateCompatibleDC(hdc);
   hdcSave   = CreateCompatibleDC(hdc);

   // Create a bitmap for each DC. DCs are required for a number of
   // GDI functions.

   // Monochrome DC
   bmAndBack   = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);

   // Monochrome DC
   bmAndObject = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);

   bmAndMem    = CreateCompatibleBitmap(hdc, ptSize.x, ptSize.y);
   bmSave      = CreateCompatibleBitmap(hdc, ptSize.x, ptSize.y);

   // Each DC must select a bitmap object to store pixel data.
   bmBackOld   = (HBITMAP)SelectObject(hdcBack, bmAndBack);
   bmObjectOld = (HBITMAP)SelectObject(hdcObject, bmAndObject);
   bmMemOld    = (HBITMAP)SelectObject(hdcMem, bmAndMem);
   bmSaveOld   = (HBITMAP)SelectObject(hdcSave, bmSave);

   // Set proper mapping mode.
   SetMapMode(hdcTemp, GetMapMode(hdc));

   // Save the bitmap sent here, because it will be overwritten.
   BitBlt(hdcSave, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);

   // Set the background color of the source DC to the color.
   // contained in the parts of the bitmap that should be transparent
   cColor = SetBkColor(hdcTemp, cTransparentColor);

   // Create the object mask for the bitmap by performing a BitBlt
   // from the source bitmap to a monochrome bitmap.
   BitBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
          SRCCOPY);

   // Set the background color of the source DC back to the original
   // color.
   SetBkColor(hdcTemp, cColor);

   // Create the inverse of the object mask.
   BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0,
          NOTSRCCOPY);

   // Copy the background of the main DC to the destination.
   BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdc, xStart, yStart,
          SRCCOPY);

   // Mask out the places where the bitmap will be placed.
   BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND);

   // Mask out the transparent colored pixels on the bitmap.
   BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);

   // XOR the bitmap with the background on the destination DC.
   BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);

   // Copy the destination to the screen.
   BitBlt(hdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, 0, 0,
          SRCCOPY);

   // Place the original bitmap back into the bitmap sent here.
   BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcSave, 0, 0, SRCCOPY);

   // Delete the memory bitmaps.
   DeleteObject(SelectObject(hdcBack, bmBackOld));
   DeleteObject(SelectObject(hdcObject, bmObjectOld));
   DeleteObject(SelectObject(hdcMem, bmMemOld));
   DeleteObject(SelectObject(hdcSave, bmSaveOld));

   // Delete the memory DCs.
   DeleteDC(hdcMem);
   DeleteDC(hdcBack);
   DeleteDC(hdcObject);
   DeleteDC(hdcSave);
   DeleteDC(hdcTemp);
}

/************************************************************************* 
 * 
 * Fade() 
 * 
 * Parameters: 
 * 
 * int nDeltaR   -  change value of Red
 * int nDeltaG   -  change value of Green
 * int nDeltaB   -  change value of Blue
 * BYTE rm		 -  maxinum value of Red
 * BYTE gm		 -  maxinum value of Green
 * BYTE bm		 -  maxinum value of Blue
 * BYTE *r		 -  pointer of value of Red after change
 * BYTE *g		 -  pointer of value of Green after change
 * BYTE *b		 -  pointer of value of Blue after change
 * 
 * Return Value: 
 *
 * BOOL          -  TRUE if success, FALSE is failue
 * 
 * Description: 
 * 
 * This function change the value of R, G, b
 * 
 ************************************************************************/
BOOL Fade(int nDeltaR, int nDeltaG, int nDeltaB, 
		  BYTE rm, BYTE gm, BYTE bm,
		  BYTE *r, BYTE *g, BYTE *b)
{
	int R = *r + nDeltaR;
	int G = *g + nDeltaG;
	int B = *b + nDeltaB;

	if (nDeltaR < 0)
		*r = (BYTE)BOUND(R, rm, 255);
	else
		*r = (BYTE)BOUND(R, 0, rm);
	if (nDeltaG < 0)
		*g = (BYTE)BOUND(G, gm, 255);
	else
		*g = (BYTE)BOUND(G, 0, gm);
	if (nDeltaB < 0)
		*b = (BYTE)BOUND(B, bm, 255);
	else
		*b = (BYTE)BOUND(B, 0, bm);

	if (*r==rm && *g==gm && *b==bm)
		return TRUE;
	else
		return FALSE;
}


/************************************************************************* 
 * 
 * Delay() 
 * 
 * Parameters: 
 * 
 * DWORD dwDelayTime   - ms to delay
 * 
 * Return Value: none
 * 
 * Description: 
 * 
 * This function Delay specified ms before perform next instruction 
 * 
 ************************************************************************/ 
void Delay(DWORD dwDelayTime)
{
    DWORD dwTimeBegin, dwTimeEnd;
    dwTimeBegin = timeGetTime();
    do
	{
        dwTimeEnd = timeGetTime();
	} while (dwTimeEnd - dwTimeBegin < dwDelayTime);
}

/************************************************************************* 
 * 
 * CopyHandle() 
 * 
 * Parameters: 
 * 
 * HANDLE h         - source handle
 * 
 * Return Value: 
 * 
 * HANDLE           - duplicated handle
 * 
 * Description: 
 * 
 * Copy memory handle to another
 ************************************************************************/ 
HANDLE CopyHandle(HANDLE h)
{
	if (h == NULL)
		return NULL;

	DWORD  dwLen = ::GlobalSize((HGLOBAL)h);
	HANDLE hCopy = ::GlobalAlloc(GHND, dwLen);
	if (hCopy == NULL)
		return NULL;

	void* lpCopy = ::GlobalLock((HGLOBAL) hCopy);
	void* lp     = ::GlobalLock((HGLOBAL) h);
	::CopyMemory(lpCopy, lp, dwLen);
	::GlobalUnlock(hCopy);
	::GlobalUnlock(h);

	return hCopy;
}

/************************************************************************* 
 * 
 * CropBitmap() 
 * 
 * Parameters: 
 * 
 * HBITMAP hBitmap - handle of DDB to be cropped
 * LPRECT lpRect   - specified rectangle to crop
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new bitmap 
 * 
 * Description: 
 * 
 * This function crop the specified rectangle in DDB, and return
 * the cropped DDB in a new DIB handle, let the source DDB unchanged
 * 
 ************************************************************************/ 
HBITMAP CropBitmap(HBITMAP hBitmap, LPRECT lpRect)
{
	WaitCursorBegin();

	// Get Curent DC
	HDC hDC = GetDC(NULL);
	// create bitmap
	HBITMAP hNewBmp = CreateCompatibleBitmap(hDC, RECTWIDTH(lpRect), RECTHEIGHT(lpRect));
	if (hNewBmp == NULL)
	{
		ReleaseDC(NULL, hDC);
		WaitCursorEnd();
		return NULL;
	}
	// Create memory DC
	HDC hMemDC = CreateCompatibleDC(hDC);
	if (hMemDC == NULL)
	{
		ReleaseDC(NULL, hDC);
		DeleteObject(hNewBmp);
		WaitCursorEnd();
		return NULL;
	}
	HDC hSrcMemDC = CreateCompatibleDC(hDC);
	if (hSrcMemDC == NULL)
	{
		DeleteDC(hMemDC);
		ReleaseDC(NULL, hDC);
		DeleteObject(hNewBmp);
		WaitCursorEnd();
		return NULL;
	}

	// Select new bitmap in memory DC
	HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemDC, hNewBmp);
	// select old bitmap in SrcMemDC
	HBITMAP hSrcOldBitmap = (HBITMAP)SelectObject(hSrcMemDC, hBitmap);

	// bitblt old bitmap in new bitmap 
	if (! BitBlt(hMemDC, 
				 0, 
				 0,
				 RECTWIDTH(lpRect), 
				 RECTHEIGHT(lpRect),
				 hSrcMemDC,
				 lpRect->left,
				 lpRect->top,
				 SRCCOPY))
	{
		SelectObject(hMemDC, hOldBitmap);
		SelectObject(hSrcMemDC, hSrcOldBitmap);
		DeleteDC(hMemDC);
		DeleteDC(hSrcMemDC);
		ReleaseDC(NULL, hDC);
		DeleteObject(hNewBmp);
		WaitCursorEnd();
		return NULL;
	}
	
	// restore
	SelectObject(hMemDC, hOldBitmap);
	SelectObject(hSrcMemDC, hSrcOldBitmap);
	DeleteDC(hMemDC);
	DeleteDC(hSrcMemDC);
	ReleaseDC(NULL, hDC);
	WaitCursorEnd();

	// return
	return hNewBmp;
}

/************************************************************************* 
 * 
 * CropDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB       - handle of DIB to be cropped
 * LPRECT lpRect   - specified rectangle to crop
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function crop the specified rectangle in DIB, and return
 * the cropped DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB CropDIB(HDIB hDIB, LPRECT lpRect)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits, lpResult; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize; 
	int			 nWidth, nHeight;
	HDIB		 hNewDIB;
	DWORD		 dwSize;

	WaitCursorBegin();

	// Get DIB pointer
	if (! hDIB)
	{
		WaitCursorEnd();
		return NULL;
	}
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)GlobalLock(hDIB);
	if (! lpSrcDIB)
	{
		WaitCursorEnd();
		return NULL;
	}

    // Allocate and fill out a BITMAPINFO struct for the new DIB 
    dwTargetHeaderSize = sizeof( BITMAPINFOHEADER ) + PaletteSize(lpSrcDIB); 
    lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); 
	memcpy(lpbmi, lpSrcDIB, dwTargetHeaderSize);
	nWidth = RECTWIDTH(lpRect);
	nHeight = RECTHEIGHT(lpRect);
    lpbmi->bmiHeader.biWidth = nWidth; 
    lpbmi->bmiHeader.biHeight = nHeight; 
 
    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    lpbmi->bmiHeader.biSizeImage = dwTargetBitsSize; 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
	// put old bitmap in new bitmap 
    BitBlt(hTargetDC, 0, 0, nWidth, nHeight, hSourceDC, lpRect->left, lpRect->top, SRCCOPY); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // Allocate enough memory for the new CF_DIB, and copy bits 
	dwSize = dwTargetHeaderSize + dwTargetBitsSize;
	hNewDIB = GlobalAlloc(GHND, dwSize);
    lpResult = (LPBYTE)GlobalLock(hNewDIB);//malloc( dwTargetHeaderSize + dwTargetBitsSize ); 
    memcpy( lpResult, lpbmi, dwTargetHeaderSize ); 
    memcpy( FindDIBBits( (LPBYTE)lpResult ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
    free( lpbmi ); 
	GlobalUnlock(hDIB);
	GlobalUnlock(hNewDIB);
	WaitCursorEnd();
 
    return hNewDIB;
}

/************************************************************************* 
 * 
 * CutDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB       - handle of DIB to be cut
 * LPRECT lpRect   - specified rectangle to cut off
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function cut off the specified rectangle in DIB, and return
 * the cut DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB CutDIB(HDIB hDIB, LPRECT lpRect)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpTargetBits; 
    HDC			 hDC = NULL, hTargetDC; 
    HBITMAP      hTargetBitmap, hOldTargetBitmap; 
    DWORD        dwTargetBitsSize; 
	HDIB		 hNewDIB;

	WaitCursorBegin();

	// Get new DIB handle
	if (! hDIB)
	{
		WaitCursorEnd();
		return NULL;
	}
	hNewDIB = CopyHandle(hDIB);
	if (! hNewDIB)
	{
		WaitCursorEnd();
		return NULL;
	}

    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    lpbmi = (LPBITMAPINFO)GlobalLock(hNewDIB); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpTargetBits, FindDIBBits((LPBYTE)lpbmi), dwTargetBitsSize ); 

    // Select DIBSections into DCs 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
	// clear rectangle specified
	HBRUSH hBrush = CreateSolidBrush(RGB(255,255,255));
	HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255,255,255));
	HBRUSH hOldBrush = (HBRUSH)SelectObject(hTargetDC, hBrush);
	HPEN hOldPen = (HPEN)SelectObject(hTargetDC, hPen);
	Rectangle(hTargetDC, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
	SelectObject(hTargetDC, hOldBrush);
	SelectObject(hTargetDC, hOldPen);
 
    // Clean up and delete the DCs 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // copy bits 
    memcpy( FindDIBBits( (LPBYTE)lpbmi ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
	GlobalUnlock(hNewDIB);
	WaitCursorEnd();
 
    return hNewDIB;
}

/************************************************************************* 
 * 
 * MergeDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB1       - handle of DIB to be merged to
 * HDIB hDIB2       - handle of DIB to be merged from
 * POINT ptTopLeft  - start merge position in hDIB1
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function merge the second DIB to the first DIB, and return
 * the merged DIB in a new DIB handle, let the source DIBs unchanged
 * 
 ************************************************************************/ 
HDIB MergeDIB(HDIB hDIB1, HDIB hDIB2, POINT ptTopLeft)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwTargetBitsSize, dwSourceBitsSize; 
	HDIB		 hNewDIB;
	int			 nWidth, nHeight;

	WaitCursorBegin();

	// Get DIB pointer
	if (! hDIB1 || ! hDIB2)
	{
		WaitCursorEnd();
		return NULL;
	}

	// new DIB
	hNewDIB = CopyHandle(hDIB1);
	if (! hNewDIB)
	{
		WaitCursorEnd();
		return NULL;
	}
    // New DIB buffer
    lpbmi = (LPBITMAPINFO)GlobalLock(hNewDIB);
	if (! lpbmi)
	{
		WaitCursorBegin();
		return NULL;
	}
	// DIB2 buffer
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)GlobalLock(hDIB2);
	
	if (! lpSrcDIB)
	{
		GlobalUnlock(hNewDIB);
		DestroyDIB(hNewDIB);
		WaitCursorBegin();
		return NULL;
	}
	nWidth = DIBWidth(lpSrcDIB);
	nHeight = DIBHeight(lpSrcDIB);

    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    memcpy( lpTargetBits, FindDIBBits((LPBYTE)lpbmi), dwTargetBitsSize ); 

    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
	// Merge
    BitBlt(hTargetDC, ptTopLeft.x, ptTopLeft.y, nWidth, nHeight, hSourceDC, 0, 0, SRCCOPY); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // and copy bits 
    memcpy( FindDIBBits((LPBYTE)lpbmi), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
	GlobalUnlock(hDIB2);
	GlobalUnlock(hNewDIB);
	WaitCursorEnd();
 
    return hNewDIB;
}

/************************************************************************* 
 * 
 * TransparentMergeDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB1       - handle of DIB to be merged to
 * HDIB hDIB2       - handle of DIB to be merged from
 * POINT ptTopLeft  - start merge position in hDIB1
 * COLORREF crTransparent - transparent color
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function merge the second DIB to the first DIB with transparent of specified
 * color, and return the merged DIB in a new DIB handle, let the source DIBs unchanged
 * 
 ************************************************************************/ 
HDIB TransparentMergeDIB(HDIB hDIB1, HDIB hDIB2, POINT ptTopLeft, COLORREF crTransparent)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpTargetBits; 
    HDC			 hDC = NULL, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap; 
    DWORD        dwTargetBitsSize; 
	HDIB		 hNewDIB;
	int			 nWidth, nHeight;

	// Get DIB pointer
	if (! hDIB1 || ! hDIB2)
		return NULL;

	// new DIB
	hNewDIB = CopyHandle(hDIB1);
	if (! hNewDIB)
		return NULL;

    // New DIB buffer
    lpbmi = (LPBITMAPINFO)GlobalLock(hNewDIB);
	if (! lpbmi)
		return NULL;

	// DIB2 
	hSourceBitmap = DIBToBitmap(hDIB2, NULL);
	if (! hSourceBitmap)
	{
		GlobalUnlock(hNewDIB);
		DestroyDIB(hNewDIB);
		return NULL;
	}
	BITMAP bm;
	GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
	nWidth = bm.bmWidth;
	nHeight = bm.bmHeight;

    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpTargetBits, FindDIBBits((LPBYTE)lpbmi), dwTargetBitsSize ); 

    // Select DIBSections into DCs 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
    // Set the color tables for the DIBSections 
    if( lpbmi->bmiHeader.biBitCount <= 8 ) 
        SetDIBColorTable( hTargetDC, 0, 1 << lpbmi->bmiHeader.biBitCount, lpbmi->bmiColors ); 
 
	// Merge
	DrawTransparentBitmap(hTargetDC, hSourceBitmap, ptTopLeft.x, ptTopLeft.y, crTransparent);

    // Clean up and delete the DCs 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // and copy bits 
    memcpy( FindDIBBits((LPBYTE)lpbmi), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
	GlobalUnlock(hDIB2);
	GlobalUnlock(hNewDIB);
 
    return hNewDIB;
}

// RotateBitmapNT	- Create a new bitmap with rotated image
// Returns		- Returns new bitmap with rotated image
// hBitmap		- Bitmap to rotate
// fDegrees		- Angle of rotation in degree
// clrBack		- Color of pixels in the resulting bitmap that do
//			  not get covered by source pixels
HBITMAP RotateBitmapNT(HBITMAP hBitmap, double fDegrees, COLORREF clrBack)
{
	// Create a memory DC compatible with the display
	CDC sourceDC, destDC;
	sourceDC.CreateCompatibleDC( NULL );
	destDC.CreateCompatibleDC( NULL );

	// Get logical coordinates
	BITMAP bm;
	::GetObject( hBitmap, sizeof( bm ), &bm );
	
	// Convert angle degree to radians 
	#define PI		3.1415926
	double radians = (fDegrees/90.0)*(PI/2);

	// Compute the cosine and sine only once
	float cosine = (float)cos(radians);
	float sine = (float)sin(radians);

	// Compute dimensions of the resulting bitmap
	// First get the coordinates of the 3 corners other than origin
	int x1 = (int)(bm.bmHeight * sine);
	int y1 = (int)(bm.bmHeight * cosine);
	int x2 = (int)(bm.bmWidth * cosine + bm.bmHeight * sine);
	int y2 = (int)(bm.bmHeight * cosine - bm.bmWidth * sine);
	int x3 = (int)(bm.bmWidth * cosine);
	int y3 = (int)(-bm.bmWidth * sine);

	int minx = min(0,min(x1, min(x2,x3)));
	int miny = min(0,min(y1, min(y2,y3)));
	int maxx = max(0,max(x1, max(x2,x3)));
	int maxy = max(0,max(y1, max(y2,y3)));

	int w = maxx - minx;
	int h = maxy - miny;

	// Create a bitmap to hold the result
	HBITMAP hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL), w, h);

	HBITMAP hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap );
	HBITMAP hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult );

	// Draw the background color before we change mapping mode
	HBRUSH hbrBack = CreateSolidBrush( clrBack );
	HBRUSH hbrOld = (HBRUSH)::SelectObject( destDC.m_hDC, hbrBack );
	destDC.PatBlt( 0, 0, w, h, PATCOPY );
	::DeleteObject( ::SelectObject( destDC.m_hDC, hbrOld ) );

	// We will use world transform to rotate the bitmap
	SetGraphicsMode(destDC.m_hDC, GM_ADVANCED);
	XFORM xform;
	xform.eM11 = cosine;
	xform.eM12 = -sine;
	xform.eM21 = sine;
	xform.eM22 = cosine;
	xform.eDx = (float)-minx;
	xform.eDy = (float)-miny;

	SetWorldTransform( destDC.m_hDC, &xform );

	// Now do the actual rotating - a pixel at a time
	destDC.BitBlt(0,0,bm.bmWidth, bm.bmHeight, &sourceDC, 0, 0, SRCCOPY );

	// Restore DCs
	::SelectObject( sourceDC.m_hDC, hbmOldSource );
	::SelectObject( destDC.m_hDC, hbmOldDest );

	return hbmResult;
}

// RotateBitmap	- Create a new bitmap with rotated image
// Returns		- Returns new bitmap with rotated image
// hBitmap		- Bitmap to rotate
// fDegrees		- Angle of rotation in degree
// clrBack		- Color of pixels in the resulting bitmap that do
//			  not get covered by source pixels
// Note			- If the bitmap uses colors not in the system palette 
//			  then the result is unexpected. You can fix this by
//			  adding an argument for the logical palette.
HBITMAP RotateBitmap(HBITMAP hBitmap, double fDegrees, COLORREF clrBack)
{
	// Create a memory DC compatible with the display
	CDC sourceDC, destDC;
	sourceDC.CreateCompatibleDC( NULL );
	destDC.CreateCompatibleDC( NULL );

	// Get logical coordinates
	BITMAP bm;
	::GetObject( hBitmap, sizeof( bm ), &bm );

	// Convert angle degree to radians 
	#define PI		3.1415926
	double radians = (fDegrees/90.0)*(PI/2);

	// Compute the cosine and sine only once
	float cosine = (float)cos(radians);
	float sine = (float)sin(radians);

	// Compute dimensions of the resulting bitmap
	// First get the coordinates of the 3 corners other than origin
	int x1 = (int)(-bm.bmHeight * sine);
	int y1 = (int)(bm.bmHeight * cosine);
	int x2 = (int)(bm.bmWidth * cosine - bm.bmHeight * sine);
	int y2 = (int)(bm.bmHeight * cosine + bm.bmWidth * sine);
	int x3 = (int)(bm.bmWidth * cosine);
	int y3 = (int)(bm.bmWidth * sine);

	int minx = min(0,min(x1, min(x2,x3)));
	int miny = min(0,min(y1, min(y2,y3)));
	int maxx = max(x1, max(x2,x3));
	int maxy = max(y1, max(y2,y3));

	int w = maxx - minx;
	int h = maxy - miny;


	// Create a bitmap to hold the result
	HBITMAP hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL), w, h);

	HBITMAP hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap );
	HBITMAP hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult );

	// Draw the background color before we change mapping mode
	HBRUSH hbrBack = CreateSolidBrush( clrBack );
	HBRUSH hbrOld = (HBRUSH)::SelectObject( destDC.m_hDC, hbrBack );
	destDC.PatBlt( 0, 0, w, h, PATCOPY );
	::DeleteObject( ::SelectObject( destDC.m_hDC, hbrOld ) );

	// Set mapping mode so that +ve y axis is upwords
	sourceDC.SetMapMode(MM_ISOTROPIC);
	sourceDC.SetWindowExt(1,1);
	sourceDC.SetViewportExt(1,-1);
	sourceDC.SetViewportOrg(0, bm.bmHeight-1);

	destDC.SetMapMode(MM_ISOTROPIC);
	destDC.SetWindowExt(1,1);
	destDC.SetViewportExt(1,-1);
	destDC.SetWindowOrg(minx, maxy);

	// Now do the actual rotating - a pixel at a time
	// Computing the destination point for each source point
	// will leave a few pixels that do not get covered
	// So we use a reverse transform - e.i. compute the source point
	// for each destination point

	for( int y = miny; y < maxy; y++ )
	{
		for( int x = minx; x < maxx; x++ )
		{
			int sourcex = (int)(x*cosine + y*sine);
			int sourcey = (int)(y*cosine - x*sine);
			if( sourcex >= 0 && sourcex < bm.bmWidth && sourcey >= 0 
					&& sourcey < bm.bmHeight )
				destDC.SetPixel(x,y,sourceDC.GetPixel(sourcex,sourcey));
		}
	}

	// Restore DCs
	::SelectObject( sourceDC.m_hDC, hbmOldSource );
	::SelectObject( destDC.m_hDC, hbmOldDest );

	return hbmResult;
}

// RotateDIB	- Create a new bitmap with rotated image
// Returns		- Returns new bitmap with rotated image
// hDIB			- Device-independent bitmap to rotate
// fDegrees		- Angle of rotation in degree
// clrBack		- Color of pixels in the resulting bitmap that do
//			  not get covered by source pixels
HDIB RotateDIB(HDIB hDIB, double fDegrees, COLORREF clrBack)
{
	WaitCursorBegin();

	// Get source bitmap info
	LPBITMAPINFO lpBmInfo = (LPBITMAPINFO)GlobalLock(hDIB);
	int bpp = lpBmInfo->bmiHeader.biBitCount;		// Bits per pixel
	
	int nColors = lpBmInfo->bmiHeader.biClrUsed ? lpBmInfo->bmiHeader.biClrUsed : 
					1 << bpp;
	int nWidth = lpBmInfo->bmiHeader.biWidth;
	int nHeight = lpBmInfo->bmiHeader.biHeight;
	int nRowBytes = ((((nWidth * bpp) + 31) & ~31) / 8);

	// Make sure height is positive and biCompression is BI_RGB or BI_BITFIELDS
	DWORD compression = lpBmInfo->bmiHeader.biCompression;
	if( nHeight < 0 || (compression!=BI_RGB))
	{
		GlobalUnlock(hDIB);
		WaitCursorEnd();
		return NULL;
	}

	LPBYTE lpDIBBits = FindDIBBits((LPBYTE)lpBmInfo);
    
	// Convert angle degree to radians 
	#define PI		3.1415926
	double radians = (fDegrees/90.0)*(PI/2);

	// Compute the cosine and sine only once
	float cosine = (float)cos(radians);
	float sine = (float)sin(radians);

	// Compute dimensions of the resulting bitmap
	// First get the coordinates of the 3 corners other than origin
	int x1 = (int)(-nHeight * sine);
	int y1 = (int)(nHeight * cosine);
	int x2 = (int)(nWidth * cosine - nHeight * sine);
	int y2 = (int)(nHeight * cosine + nWidth * sine);
	int x3 = (int)(nWidth * cosine);
	int y3 = (int)(nWidth * sine);

	int minx = min(0,min(x1, min(x2,x3)));
	int miny = min(0,min(y1, min(y2,y3)));
	int maxx = max(x1, max(x2,x3));
	int maxy = max(y1, max(y2,y3));

	int w = maxx - minx;
	int h = maxy - miny;

	// Create a DIB to hold the result
	int nResultRowBytes = ((((w * bpp) + 31) & ~31) / 8);
	long len = nResultRowBytes * h;
	int nHeaderSize = ((LPBYTE)lpDIBBits-(LPBYTE)lpBmInfo) ;
	HANDLE hDIBResult = GlobalAlloc(GHND,len+nHeaderSize);
	// Initialize the header information
	LPBITMAPINFO lpBmInfoResult = (LPBITMAPINFO)GlobalLock(hDIBResult);
	memcpy( (void*)lpBmInfoResult, (void*)lpBmInfo, nHeaderSize);
	lpBmInfoResult->bmiHeader.biWidth = w;
	lpBmInfoResult->bmiHeader.biHeight = h;
	lpBmInfoResult->bmiHeader.biSizeImage = len;

	LPBYTE lpDIBBitsResult = FindDIBBits((LPBYTE)lpBmInfoResult);

	// Get the back color value (index)
	ZeroMemory( lpDIBBitsResult, len );
	DWORD dwBackColor;
	switch(bpp)
	{
	case 1:	//Monochrome
		if( clrBack == RGB(255,255,255) )
			memset( lpDIBBitsResult, 0xff, len );
		break;
	case 4:
	case 8:	//Search the color table
		int i;
		for(i = 0; i < nColors; i++ )
		{
			if( lpBmInfo->bmiColors[i].rgbBlue ==  GetBValue(clrBack)
				&& lpBmInfo->bmiColors[i].rgbGreen ==  GetGValue(clrBack)
				&& lpBmInfo->bmiColors[i].rgbRed ==  GetRValue(clrBack) )
			{
				if(bpp==4) i = i | i<<4;
				memset( lpDIBBitsResult, i, len );
				break;
			}
		}
		// If not match found the color remains black
		break;
	case 16:
		// Windows95 supports 5 bits each for all colors or 5 bits for red & blue
		// and 6 bits for green - Check the color mask for RGB555 or RGB565
		if( *((DWORD*)lpBmInfo->bmiColors) == 0x7c00 )
		{
			// Bitmap is RGB555
			dwBackColor = ((GetRValue(clrBack)>>3) << 10) + 
					((GetRValue(clrBack)>>3) << 5) +
					(GetBValue(clrBack)>>3) ;
		}
		else
		{
			// Bitmap is RGB565
			dwBackColor = ((GetRValue(clrBack)>>3) << 11) + 
					((GetRValue(clrBack)>>2) << 5) +
					(GetBValue(clrBack)>>3) ;
		}
		break;
	case 24:
	case 32:
		dwBackColor = (((DWORD)GetRValue(clrBack)) << 16) | 
				(((DWORD)GetGValue(clrBack)) << 8) |
				(((DWORD)GetBValue(clrBack)));
		break;
	}


	// Now do the actual rotating - a pixel at a time
	// Computing the destination point for each source point
	// will leave a few pixels that do not get covered
	// So we use a reverse transform - e.i. compute the source point
	// for each destination point

	for( int y = 0; y < h; y++ )
	{
		for( int x = 0; x < w; x++ )
		{
			int sourcex = (int)((x+minx)*cosine + (y+miny)*sine);
			int sourcey = (int)((y+miny)*cosine - (x+minx)*sine);
			if( sourcex >= 0 && sourcex < nWidth && sourcey >= 0 
				&& sourcey < nHeight )
			{
				// Set the destination pixel
				switch(bpp)
				{
					BYTE mask;
				case 1:		//Monochrome
					mask = *((LPBYTE)lpDIBBits + nRowBytes*sourcey + 
						sourcex/8) & (0x80 >> sourcex%8);
					//Adjust mask for destination bitmap
					mask = mask ? (0x80 >> x%8) : 0;
					*((LPBYTE)lpDIBBitsResult + nResultRowBytes*(y) + 
								(x/8)) &= ~(0x80 >> x%8);
					*((LPBYTE)lpDIBBitsResult + nResultRowBytes*(y) + 
								(x/8)) |= mask;
					break;
				case 4:
					mask = *((LPBYTE)lpDIBBits + nRowBytes*sourcey + 
						sourcex/2) & ((sourcex&1) ? 0x0f : 0xf0);
					//Adjust mask for destination bitmap
					if( (sourcex&1) != (x&1) )
						mask = (mask&0xf0) ? (mask>>4) : (mask<<4);
					*((LPBYTE)lpDIBBitsResult + nResultRowBytes*(y) + 
							(x/2)) &= ~((x&1) ? 0x0f : 0xf0);
					*((LPBYTE)lpDIBBitsResult + nResultRowBytes*(y) + 
							(x/2)) |= mask;
					break;
				case 8:
					BYTE pixel ;
					pixel = *((LPBYTE)lpDIBBits + nRowBytes*sourcey + 
							sourcex);
					*((LPBYTE)lpDIBBitsResult + nResultRowBytes*(y) + 
							(x)) = pixel;
					break;
				case 16:
					DWORD dwPixel;
					dwPixel = *((LPWORD)((LPBYTE)lpDIBBits + 
							nRowBytes*sourcey + sourcex*2));
					*((LPWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*2)) = (WORD)dwPixel;
					break;
				case 24:
					dwPixel = *((LPDWORD)((LPBYTE)lpDIBBits + 
						nRowBytes*sourcey + sourcex*3)) & 0xffffff;
					*((LPDWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*3)) |= dwPixel;
					break;
				case 32:
					dwPixel = *((LPDWORD)((LPBYTE)lpDIBBits + 
						nRowBytes*sourcey + sourcex*4));
					*((LPDWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*4)) = dwPixel;
				}
			}
			else 
			{
				// Draw the background color. The background color
				// has already been drawn for 8 bits per pixel and less
				switch(bpp)
				{
				case 16:
					*((LPWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*2)) = 
						(WORD)dwBackColor;
					break;
				case 24:
					*((LPDWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*3)) |= dwBackColor;
					break;
				case 32:
					*((LPDWORD)((LPBYTE)lpDIBBitsResult + 
						nResultRowBytes*y + x*4)) = dwBackColor;
					break;
				}
			}
		}
	}

	GlobalUnlock(hDIB);
	GlobalUnlock(hDIBResult);
	WaitCursorEnd();

	return hDIBResult;
}

/************************************************************************* 
 * 
 * RotateDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB				 - handle of DIB to rotate
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function rotate DIB 90 degree counter clockwise, and return
 * the rotated DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB RotateDIB(HDIB hDib)
{
	WaitCursorBegin();

	// old DIB
	LPBYTE lpDIBSrc = (LPBYTE)GlobalLock(hDib);

	DWORD lSrcWidth = DIBWidth(lpDIBSrc);
	DWORD lSrcHeight = DIBHeight(lpDIBSrc);
	WORD wBitCount = ((LPBITMAPINFOHEADER)lpDIBSrc)->biBitCount;
	// bits position
    LPBYTE lpOldBits = FindDIBBits(lpDIBSrc);

	// get bytes/pixel, bytes/row of new DIB
	double fColorBytes = (double)((double)wBitCount/8.0);
	DWORD lSrcRowBytes = WIDTHBYTES(lSrcWidth*((DWORD)wBitCount));
	DWORD lDestRowBytes = WIDTHBYTES(lSrcHeight*((DWORD)wBitCount));

	// adjust new DIB size
	DWORD dwDataLength = GlobalSize(hDib);
	dwDataLength += lDestRowBytes*(lSrcWidth-1)+(DWORD)((lSrcHeight-1)*fColorBytes) - 
				  lSrcRowBytes*(lSrcHeight-1)+(DWORD)((lSrcWidth-1)*fColorBytes);
	HDIB hNewDib = GlobalAlloc(GHND, dwDataLength);
	if (! hNewDib)
	{
		WaitCursorEnd();
		return NULL;
	}
	// new DIB buffer
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hNewDib);
	// copy LPBITMAPINFO from old to new
	memcpy(lpDIB, lpDIBSrc, sizeof(BITMAPINFOHEADER)+PaletteSize(lpDIBSrc));
	// swap width and height
	((LPBITMAPINFOHEADER)lpDIB)->biHeight = lSrcWidth;
	((LPBITMAPINFOHEADER)lpDIB)->biWidth = lSrcHeight;
	// new bits position
	LPBYTE lpData = FindDIBBits(lpDIB);

	// trandform bits
	DWORD i, j;
	switch (wBitCount)
	{
	case 1:
		for (i=0; i<lSrcHeight; ++i)
		{
			for (j=0; j<lSrcWidth; ++j)
			{
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)/8)) &= ~(1<<(7-((lSrcHeight-i-1)%8)));
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)/8)) |= 
					((*(lpOldBits+(lSrcRowBytes*i+j/8))<<(j%8))>>7)<<(7-((lSrcHeight-i-1)%8));
			}
		}
		break;
	case 4:
		for (i=0; i<lSrcHeight; ++i)
		{
			for (j=0; j<lSrcWidth; ++j)
			{
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)/2)) &= ((lSrcHeight-i-1)%2) ? 0xf0 : 0x0f;
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)/2)) |= 
					((*(lpOldBits+(lSrcRowBytes*i+j/2))<<(j%2 ? 4 : 0))>>4)<<(((lSrcHeight-i-1)%2) ? 0 : 4);
			}
		}
		break;
	case 8:
		for (i=0; i<lSrcHeight; ++i)
		{
			for (j=0; j<lSrcWidth; ++j)
			{
				*(lpData+(lDestRowBytes*j+lSrcHeight-i-1))
					= *(lpOldBits+(lSrcRowBytes*i+j));
			}
		}
		break;
	case 24:
		for (i=0; i<lSrcHeight; ++i)
		{
			for (j=0; j<lSrcWidth; j++)
			{
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)*3))
					= *(lpOldBits+(lSrcRowBytes*i+j*3));
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)*3)+1)
					= *(lpOldBits+(lSrcRowBytes*i+j*3)+1);
				*(lpData+(lDestRowBytes*j+(lSrcHeight-i-1)*3)+2)
					= *(lpOldBits+(lSrcRowBytes*i+j*3)+2);
			}
		}
		break;
	}

	// cleanup
	GlobalUnlock(hDib);
	GlobalUnlock(hNewDib);
	WaitCursorEnd();
	
	return hNewDib;
}

/************************************************************************* 
 * 
 * FlipHorzDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB		 - handle of DIB to flip horzonly
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function flip DIB horzonly, and return
 * the flipped DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB FlipHorzDIB(HDIB hDib)
{
	WaitCursorBegin();

	// create new DIB
	DWORD dwDataLength = GlobalSize(hDib);
	HDIB hNewDib = GlobalAlloc(GHND, dwDataLength);
	if (! hNewDib)
	{
		WaitCursorEnd();
		return NULL;
	}
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hNewDib);
	if (lpDIB == NULL)
	{
		WaitCursorEnd();
		return FALSE;
	}
	// old DIB buffer
	LPBYTE lpDIBSrc = (LPBYTE)GlobalLock(hDib);

	// copy LPBITMAPINFO from old to new
	memcpy(lpDIB, lpDIBSrc, sizeof(BITMAPINFOHEADER)+PaletteSize(lpDIBSrc));
	
	DWORD lSrcWidth = DIBWidth(lpDIBSrc);
	DWORD lSrcHeight = DIBHeight(lpDIBSrc);
	WORD wBitCount = ((LPBITMAPINFOHEADER)lpDIBSrc)->biBitCount;

	// get bytesbytes/pixel, bytes/row  
	double fColorBytes = (double)((double)wBitCount/8.0);
	DWORD lSrcRowBytes = WIDTHBYTES(lSrcWidth*((DWORD)wBitCount));

	// bits position
    LPBYTE lpOldBits = FindDIBBits(lpDIBSrc);
	LPBYTE lpData = FindDIBBits(lpDIB);

	// trandform bits
	DWORD i, j;
	switch (wBitCount)
	{
	case 1:
		for (i=0; i<lSrcHeight; ++i)
			for (j=0; j<lSrcWidth; ++j)
			{
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)/8)) &= ~(1<<(7-((lSrcWidth-j-1)%8)));
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)/8)) |= 
					((*(lpOldBits+(lSrcRowBytes*i+j/8))<<(j%8))>>7)<<(7-(lSrcWidth-j-1)%8);
			}
		break;
	case 4:
		for (i=0; i<lSrcHeight; ++i)
		{
			for (j=0; j<lSrcWidth; ++j)
			{
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)/2)) &= ((lSrcWidth-j-1)%2) ? 0xf0 : 0x0f;
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)/2)) |= 
					((*(lpOldBits+(lSrcRowBytes*i+j/2))<<(j%2 ? 4 : 0))>>4)<<(((lSrcWidth-j-1)%2) ? 0 : 4);
			}
		}
		break;
	case 8:
		for (i=0; i<lSrcHeight; ++i)
			for (j=0; j<lSrcWidth; ++j)
				*(lpData+(lSrcRowBytes*i+lSrcWidth-j-1))
					= *(lpOldBits+(lSrcRowBytes*i+j));
		break;
	case 24:
		for (i=0; i<lSrcHeight; ++i)
			for (j=0; j<lSrcWidth; j++)
			{
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)*3))
					= *(lpOldBits+(lSrcRowBytes*i+j*3));
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)*3)+1)
					= *(lpOldBits+(lSrcRowBytes*i+j*3)+1);
				*(lpData+(lSrcRowBytes*i+(lSrcWidth-j-1)*3)+2)
					= *(lpOldBits+(lSrcRowBytes*i+j*3)+2);
			}
		break;
	}

	GlobalUnlock(hDib);
	GlobalUnlock(hNewDib);
	WaitCursorEnd();

	return hNewDib;
}

/************************************************************************* 
 * 
 * FlipVertDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB		 - handle of DIB to flip vertically
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function flip DIB vertically, and return
 * the flipped DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB FlipVertDIB(HDIB hDib)
{
	WaitCursorBegin();

	// create new DIB
	DWORD dwDataLength = GlobalSize(hDib);
	HDIB hNewDib = GlobalAlloc(GHND, dwDataLength);
	if (! hNewDib)
	{
		WaitCursorEnd();
		return NULL;
	}
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hNewDib);
	if (lpDIB == NULL)
	{
		WaitCursorEnd();
		return FALSE;
	}
	// old DIB buffer
	LPBYTE lpDIBSrc = (LPBYTE)GlobalLock(hDib);

	// copy LPBITMAPINFO from old to new
	memcpy(lpDIB, lpDIBSrc, sizeof(BITMAPINFOHEADER)+PaletteSize(lpDIBSrc));
	
	DWORD lSrcWidth = DIBWidth(lpDIBSrc);
	DWORD lSrcHeight = DIBHeight(lpDIBSrc);
	WORD wBitCount = ((LPBITMAPINFOHEADER)lpDIBSrc)->biBitCount;

	// get bytesbytes/pixel, bytes/row  
	double fColorBytes = (double)((double)wBitCount/8.0);
	DWORD lSrcRowBytes = WIDTHBYTES(lSrcWidth*((DWORD)wBitCount));

	// bits position
    LPBYTE lpOldBits = FindDIBBits(lpDIBSrc);
	LPBYTE lpData = FindDIBBits(lpDIB);

	// trandform bits
	for (DWORD i=0; i<lSrcHeight; ++i)
		for (DWORD j=0; j<lSrcRowBytes; ++j)
			*(lpData+(lSrcRowBytes*(lSrcHeight-i-1)+j))
				= *(lpOldBits+(lSrcRowBytes*i+j));

	GlobalUnlock(hDib);
	GlobalUnlock(hNewDib);
	WaitCursorEnd();

	return hNewDib;
}

/************************************************************************* 
 * 
 * ChangeDIBSize() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB				 - handle of DIB to zoom
 * int nWidth, int nHeight   - new size of DIB
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function zoom a DIB to specified size, and return
 * the zoomed DIB in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB ChangeDIBSize(HDIB hDIB, int nWidth, int nHeight)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits, lpResult; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize; 
	HDIB		 hNewDIB;
	DWORD		 dwSize;

	WaitCursorBegin();

	// Get DIB pointer
	if (! hDIB)
	{
		WaitCursorEnd();
		return NULL;
	}
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)GlobalLock(hDIB);
	if (! lpSrcDIB)
	{
		WaitCursorEnd();
		return NULL;
	}

    // Allocate and fill out a BITMAPINFO struct for the new DIB 
    dwTargetHeaderSize = sizeof( BITMAPINFOHEADER ) + PaletteSize(lpSrcDIB); 
    lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); 
	memcpy(lpbmi, lpSrcDIB, dwTargetHeaderSize);
    lpbmi->bmiHeader.biWidth = nWidth; 
    lpbmi->bmiHeader.biHeight = nHeight; 
 
    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    lpbmi->bmiHeader.biSizeImage = dwTargetBitsSize; 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
	// put old bitmap in new bitmap 
    SetStretchBltMode( hTargetDC, COLORONCOLOR ); 
    StretchBlt( hTargetDC, 0, 0, lpbmi->bmiHeader.biWidth, lpbmi->bmiHeader.biHeight, hSourceDC, 0, 0, lpSrcDIB->bmiHeader.biWidth, lpSrcDIB->bmiHeader.biHeight, SRCCOPY ); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // Allocate enough memory for the new CF_DIB, and copy bits 
	dwSize = dwTargetHeaderSize + dwTargetBitsSize;
	hNewDIB = GlobalAlloc(GHND, dwSize);
    lpResult = (LPBYTE)GlobalLock(hNewDIB);//malloc( dwTargetHeaderSize + dwTargetBitsSize ); 
    memcpy( lpResult, lpbmi, dwTargetHeaderSize ); 
    memcpy( FindDIBBits( (LPBYTE)lpResult ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
    free( lpbmi ); 
	GlobalUnlock(hDIB);
	GlobalUnlock(hNewDIB);
	WaitCursorEnd();
 
    return hNewDIB;
}

/************************************************************************* 
 * 
 * ChangeDIBCanvasSize() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB				 - handle of DIB to change
 * int nWidth, int nHeight   - new size of DIB
 * int nPosition			 - position of source DIB in new DIB
 * 
 * Return Value: 
 * 
 * HDIB             - Handle to new DIB 
 * 
 * Description: 
 * 
 * This function change the canvas of DIB, and put source DIB in the
 * specified position of new DIB (canvas), and return the changed DIB 
 * in a new DIB handle, let the source DIB unchanged
 * 
 ************************************************************************/ 
HDIB ChangeDIBCanvasSize(HDIB hDIB, int nWidth, int nHeight, int nPosition)
{
    LPBITMAPINFO lpbmi = NULL; 
    LPBYTE       lpSourceBits, lpTargetBits, lpResult; 
    HDC			 hDC = NULL, hSourceDC, hTargetDC; 
    HBITMAP      hSourceBitmap, hTargetBitmap, hOldTargetBitmap, hOldSourceBitmap; 
    DWORD        dwSourceBitsSize, dwTargetBitsSize, dwTargetHeaderSize; 
	HDIB		 hNewDIB;
	DWORD		 dwSize;

	WaitCursorBegin();

	// Get DIB pointer
	if (! hDIB)
	{
		WaitCursorEnd();
		return NULL;
	}
	LPBITMAPINFO lpSrcDIB = (LPBITMAPINFO)GlobalLock(hDIB);
	if (! lpSrcDIB)
	{
		WaitCursorEnd();
		return NULL;
	}

    // Allocate and fill out a BITMAPINFO struct for the new DIB 
    dwTargetHeaderSize = sizeof( BITMAPINFOHEADER ) + PaletteSize(lpSrcDIB); 
    lpbmi = (LPBITMAPINFO)malloc( dwTargetHeaderSize ); 
	memcpy(lpbmi, lpSrcDIB, dwTargetHeaderSize);
    lpbmi->bmiHeader.biWidth = nWidth; 
    lpbmi->bmiHeader.biHeight = nHeight; 
 
    // Gonna use DIBSections and BitBlt() to do the conversion, so make 'em 
	hDC = GetDC( NULL ); 
    hTargetBitmap = CreateDIBSection( hDC, lpbmi, DIB_RGB_COLORS, (VOID **)&lpTargetBits, NULL, 0 ); 
    hSourceBitmap = CreateDIBSection( hDC, lpSrcDIB, DIB_RGB_COLORS, (VOID **)&lpSourceBits, NULL, 0 ); 
    hSourceDC = CreateCompatibleDC( hDC ); 
    hTargetDC = CreateCompatibleDC( hDC ); 
 
    // Flip the bits on the source DIBSection to match the source DIB 
    dwSourceBitsSize = lpSrcDIB->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpSrcDIB->bmiHeader)); 
    dwTargetBitsSize = lpbmi->bmiHeader.biHeight * BytesPerLine((LPBYTE)&(lpbmi->bmiHeader)); 
    memcpy( lpSourceBits, FindDIBBits((LPBYTE)lpSrcDIB), dwSourceBitsSize ); 
    lpbmi->bmiHeader.biSizeImage = dwTargetBitsSize; 
 
    // Select DIBSections into DCs 
    hOldSourceBitmap = (HBITMAP)SelectObject( hSourceDC, hSourceBitmap ); 
    hOldTargetBitmap = (HBITMAP)SelectObject( hTargetDC, hTargetBitmap ); 
 
	// old bitmap position in new bitmap
	int x = 0;
	int y = 0;
	int nOldWidth = lpSrcDIB->bmiHeader.biWidth;
	int nOldHeight = lpSrcDIB->bmiHeader.biHeight; 
 	switch (nPosition)
	{
	case LEFT_UP:
		x = 0;
		y = 0;
		break;
	case CENTER_UP:
		x = (nWidth-nOldWidth)/2;
		y = 0;
		break;
	case RIGHT_UP:
		x = nWidth-nOldWidth;
		y = 0;
		break;
	case LEFT_CENTER:
		x = 0;
		y = (nHeight-nOldHeight)/2;
		break;
	case CENTER_CENTER:
		x = (nWidth-nOldWidth)/2;
		y = (nHeight-nOldHeight)/2;
		break;
	case CENTER_RIGHT:
		x = nWidth-nOldWidth;
		y = (nHeight-nOldHeight)/2;
		break;
	case LEFT_DOWN:
		x = 0;
		y = nHeight-nOldHeight;
		break;
	case CENTER_DOWN:
		x = (nWidth-nOldWidth)/2;
		y = nHeight-nOldHeight;
		break;
	case RIGHT_DOWN:
		x = nWidth-nOldWidth;
		y = nHeight-nOldHeight;
		break;
	}

	// first clear the new bitmap with whitwness
	HBRUSH hBrush = CreateSolidBrush(RGB(255,255,255));
	HPEN hPen = CreatePen(PS_SOLID, 1, RGB(255,255,255));
	HBRUSH hOldBrush = (HBRUSH)SelectObject(hTargetDC, hBrush);
	HPEN hOldPen = (HPEN)SelectObject(hTargetDC, hPen);
	Rectangle(hTargetDC, 0, 0, nWidth, nHeight);
	SelectObject(hTargetDC, hOldBrush);
	SelectObject(hTargetDC, hOldPen);
	// put old bitmap in new bitmap 
    BitBlt(hTargetDC, x, y, nOldWidth, nOldHeight, hSourceDC, 0, 0, SRCCOPY); 
 
    // Clean up and delete the DCs 
    SelectObject( hSourceDC, hOldSourceBitmap ); 
    SelectObject( hTargetDC, hOldTargetBitmap ); 
    DeleteDC( hSourceDC ); 
    DeleteDC( hTargetDC ); 
    ReleaseDC( NULL, hDC ); 
 
    // Flush the GDI batch, so we can play with the bits 
    GdiFlush(); 
 
    // Allocate enough memory for the new CF_DIB, and copy bits 
	dwSize = dwTargetHeaderSize + dwTargetBitsSize;
	hNewDIB = GlobalAlloc(GHND, dwSize);
    lpResult = (LPBYTE)GlobalLock(hNewDIB);//malloc( dwTargetHeaderSize + dwTargetBitsSize ); 
    memcpy( lpResult, lpbmi, dwTargetHeaderSize ); 
    memcpy( FindDIBBits( (LPBYTE)lpResult ), lpTargetBits, dwTargetBitsSize ); 
 
    // final cleanup 
    DeleteObject( hTargetBitmap ); 
    DeleteObject( hSourceBitmap ); 
    free( lpbmi ); 
	GlobalUnlock(hDIB);
	GlobalUnlock(hNewDIB);
	WaitCursorEnd();
 
    return hNewDIB;
}

/************************************************************************* 
 * 
 * ColorQuantizeDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB        - handle to global memory with a DIB spec 
 *                    in it followed by the DIB bits 
 * 
 * UINT nColorBits  - reduced DIB color bits
 *
 * UINT nMaxColors  - reduced DIB color number
 *
 * 
 * Return Value: 
 * 
 * HDIB             - destination DIB handle
 * 
 * Description: 
 *   Perform DIB color quatization
 * 
 ************************************************************************/ 
HDIB ColorQuantizeDIB(HDIB hDIB, UINT nColorBits, UINT nMaxColors)
{
	HPALETTE hPal = NULL;

	WaitCursorBegin();

	// color quantization palette
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hDIB);

	hPal = CreateOctreePalette(lpDIB, nMaxColors, nColorBits);

	GlobalUnlock(hDIB);

	// convert DIB to new format
	HDIB hNewDIB = ConvertDIBFormat(hDIB, nColorBits, hPal);

	WaitCursorEnd();

	return hNewDIB;
}

/************************************************************************* 
 * 
 * ConvertToGrayscale() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB        - handle to global memory with a DIB spec 
 *                    in it followed by the DIB bits 
 * 
 * int nMethod      - method used to convert color to grayscale
 *
 * double fRedWeight    - weight of Red
 *
 * double fGreenWeight	- weight of Green
 *
 * double fBlueWeight	- weight of Blue
 * 
 * Return Value: 
 * 
 * HPALETTE         - handle of result grayscale palette
 * 
 * Description: 
 *   Change DIB color table from color to grayscale
 * 
 ************************************************************************/ 
HPALETTE ConvertToGrayscale(HDIB hDib, int nMethod, 
				double fRedWeight, double fGreenWeight, double fBlueWeight)
{
	if (hDib == NULL)
		return NULL;

	BITMAPINFO* lpbi = (BITMAPINFO *)GlobalLock(hDib);
	if (! lpbi)
		return NULL;

	WaitCursorBegin();

	// get color number
	WORD wNumColors = DIBNumColors((LPBYTE)lpbi);
	if (wNumColors == 0)		// There is no palette
	{
		GlobalUnlock(hDib);
		// reduce colors to 256
		HDIB hNewDib = ColorQuantizeDIB(hDib, 8, 256);
		LPBYTE lpDIB = (LPBYTE)GlobalLock(hNewDib);
		if (! lpDIB)
		{
			WaitCursorEnd();
			return NULL;
		}
		DWORD dwSize = DIBlockSize(lpDIB);
		hDib = GlobalReAlloc(hDib, dwSize, 0);
		lpbi = (BITMAPINFO *)GlobalLock(hDib);
		CopyMemory((LPBYTE)lpbi, (LPBYTE)lpDIB, dwSize);
		GlobalUnlock(hNewDib);
		GlobalFree(hNewDib);
	}

	wNumColors = DIBNumColors((LPBYTE)lpbi);
	BYTE GrayValue = 0;
	WORD i;
	switch (nMethod)
	{
		case MAXIMUM_GRAY:
			for (i=0; i<wNumColors; i++)
				{
					GrayValue = max(max(lpbi->bmiColors[i].rgbRed, 
										lpbi->bmiColors[i].rgbGreen),
									lpbi->bmiColors[i].rgbBlue);
					lpbi->bmiColors[i].rgbRed   =
					lpbi->bmiColors[i].rgbGreen =
					lpbi->bmiColors[i].rgbBlue  = GrayValue;
				}
				break;
			case MEAN_GRAY:
				for (i=0; i<wNumColors; i++)
				{
					GrayValue = min(255, 
									(lpbi->bmiColors[i].rgbRed + 
									 lpbi->bmiColors[i].rgbGreen +
									 lpbi->bmiColors[i].rgbBlue)/3);
					lpbi->bmiColors[i].rgbRed   =
					lpbi->bmiColors[i].rgbGreen =
					lpbi->bmiColors[i].rgbBlue  = GrayValue;
				}
				break;
			case WEIGHT_GRAY:
				for (i=0; i<wNumColors; i++)
				{
					GrayValue = min(255, 
									(BYTE)(lpbi->bmiColors[i].rgbRed*fRedWeight + 
									lpbi->bmiColors[i].rgbGreen*fGreenWeight +
									lpbi->bmiColors[i].rgbBlue*fBlueWeight));
					lpbi->bmiColors[i].rgbRed   =
					lpbi->bmiColors[i].rgbGreen =
					lpbi->bmiColors[i].rgbBlue  = GrayValue;
				}
				break;
	}
	GlobalUnlock(hDib);
	WaitCursorEnd();

	return CreateDIBPalette(hDib);
}

/************************************************************************* 
 * 
 * AdjustDIBColor() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB        - handle to global memory with a DIB spec 
 *                    in it followed by the DIB bits 
 * 
 * int nColorModel  - color model type, maybe RGB, HSI, HLS or CMYK
 *
 * int v1, v2, v3   - changed value to color value
 *
 * 
 * Return Value: 
 * 
 * BOOL				- TRUE is success, else FALSE
 * 
 * Description: 
 *   Adjust DIB color by RGB, CMYK, HSI, HLS color model 
 * 
 ************************************************************************/ 
BOOL AdjustDIBColor(HDIB hDib, int nColorModel, int v1, int v2, int v3)
{
	BYTE r, g, b, c, m, y, k;
	int  dr, dg, db, dc, dm, dy;
	double dh, ds, dv, dl, h, s, v, l;

	if (hDib == NULL)
		return FALSE;
	BITMAPINFO *bmi = (BITMAPINFO *)GlobalLock(hDib);
	if (! bmi)
		return FALSE;

	WaitCursorBegin();

	switch (nColorModel)
	{
	case RGB_COLOR:
		dr = v1;
		dg = v2;
		db = v3;
		break;
	case CMYK_COLOR:
		dc = v1;
		dm = v2;
		dy = v3;
		break;
	case HSI_COLOR:
		if (v1 < 0)
			v1 += 360;
		dh = v1;
		ds = v2;
		dv = v3;
		break;
	case HLS_COLOR:
		if (v1 < 0)
			v1 += 360;
		dh = v1;
		dl = v2/100.0;
		ds = v3/100.0;
		break;
	}

	// get color number
	WORD wNumColors = DIBNumColors((LPBYTE)bmi);

	if (wNumColors)		// There is palette
	{
		WORD i;
		switch (nColorModel)
		{
			case RGB_COLOR:
				for (i=0; i<wNumColors; i++)
				{
					bmi->bmiColors[i].rgbRed = BOUND(bmi->bmiColors[i].rgbRed+dr, 0, 255);
					bmi->bmiColors[i].rgbGreen = BOUND(bmi->bmiColors[i].rgbGreen+dg, 0, 255);
					bmi->bmiColors[i].rgbBlue  = BOUND(bmi->bmiColors[i].rgbBlue+db, 0, 255);
				}
				break;
			case CMYK_COLOR:
				for (i=0; i<wNumColors; i++)
				{
					r = bmi->bmiColors[i].rgbRed;
					g = bmi->bmiColors[i].rgbGreen;
					b = bmi->bmiColors[i].rgbBlue;
					RGBtoCMYK(r, g, b, &c, &m, &y, &k);
					c += dc;
					m += dm;
					y += dy;
					CMYKtoRGB(c, m, y, k, &r, &g, &b);
					bmi->bmiColors[i].rgbRed   = r;
					bmi->bmiColors[i].rgbGreen = g;
					bmi->bmiColors[i].rgbBlue  = b;
				}
				break;
			case HSI_COLOR:
				for (i=0; i<wNumColors; i++)
				{
					r = bmi->bmiColors[i].rgbRed;
					g = bmi->bmiColors[i].rgbGreen;
					b = bmi->bmiColors[i].rgbBlue;
					RGBtoHSI(r, g, b, &h, &s, &v);
					h += dh;
					s += ds;
					v += dv;
					HSItoRGB(h, s, v, &r, &g, &b);
					bmi->bmiColors[i].rgbRed   = r;
					bmi->bmiColors[i].rgbGreen = g;
					bmi->bmiColors[i].rgbBlue  = b;
				}
				break;
			case HLS_COLOR:
				for (i=0; i<wNumColors; i++)
				{
					r = bmi->bmiColors[i].rgbRed;
					g = bmi->bmiColors[i].rgbGreen;
					b = bmi->bmiColors[i].rgbBlue;
					RGBtoHLS(r, g, b, &h, &l, &s);
					if (h != UNDEFINED)
						h = BOUND(h+dh, 0.0, 360.0);
					l = BOUND(l+dl, 0.0, 1.0);
					s = BOUND(s+ds, 0.0, 1.0);
					HLStoRGB(h, l, s, &r, &g, &b);
					bmi->bmiColors[i].rgbRed   = r;
					bmi->bmiColors[i].rgbGreen = g;
					bmi->bmiColors[i].rgbBlue  = b;
				}
				break;
		}
	}
	else		// No palette
	{
		// bits position
		LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)bmi;
		LPBYTE lpBits = (LPBYTE)lpbi + lpbi->biSize;
		int nDelta = WIDTHBYTES(lpbi->biBitCount*lpbi->biWidth) - lpbi->biWidth*lpbi->biBitCount/8;
		int nx, ny;
		switch (nColorModel)
		{
			case RGB_COLOR:
				for (ny=0; ny<lpbi->biHeight; ny++)
				{
					for (nx=0; nx<lpbi->biWidth; nx++)
					{
						b = (BYTE)*(lpBits);
						g = (BYTE)*(lpBits+1);
						r = (BYTE)*(lpBits+2);
						*lpBits++ = BOUND(b+db, 0, 255);
						*lpBits++ = BOUND(g+dg, 0, 255);
						*lpBits++ = BOUND(r+dr, 0, 255);
					}
					lpBits += nDelta; 
				}
				break;
			case CMYK_COLOR:
				for (ny=0; ny<lpbi->biHeight; ny++)
				{
					for (nx=0; nx<lpbi->biWidth; nx++)
					{
						b = (BYTE)*(lpBits);
						g = (BYTE)*(lpBits+1);
						r = (BYTE)*(lpBits+2);
						RGBtoCMYK(r, g, b, &c, &m, &y, &k);
						c += dc;
						m += dm;
						y += dy;
						CMYKtoRGB(c, m, y, k, &r, &g, &b);
						*lpBits++ = b;
						*lpBits++ = g;
						*lpBits++ = r;
					}
					lpBits += nDelta; 
				}
				break;
			case HSI_COLOR:
				for (ny=0; ny<lpbi->biHeight; ny++)
				{
					for (nx=0; nx<lpbi->biWidth; nx++)
					{
						b = (BYTE)*(lpBits);
						g = (BYTE)*(lpBits+1);
						r = (BYTE)*(lpBits+2);
						RGBtoHSI(r, g, b, &h, &s, &v);
						h += dh;
						s += ds;
						v += dv;
						HSItoRGB(h, s, v, &r, &g, &b);
						*lpBits++ = b;
						*lpBits++ = g;
						*lpBits++ = r;
					}
					lpBits += nDelta; 
				}
				break;
			case HLS_COLOR:
				for (ny=0; ny<lpbi->biHeight; ny++)
				{
					for (nx=0; nx<lpbi->biWidth; nx++)
					{
						b = (BYTE)*(lpBits);
						g = (BYTE)*(lpBits+1);
						r = (BYTE)*(lpBits+2);
						RGBtoHLS(r, g, b, &h, &l, &s);
						if (h != UNDEFINED)
							h = BOUND(h+dh, 0.0, 360.0);
						l = BOUND(l+dl, 0.0, 1.0);
						s = BOUND(s+ds, 0.0, 1.0);
						HLStoRGB(h, l, s, &r, &g, &b);
						*lpBits++ = b;
						*lpBits++ = g;
						*lpBits++ = r;
					}
					lpBits += nDelta; 
				}
				break;
		}
	}

	GlobalUnlock(hDib);
	WaitCursorEnd();
	return TRUE;
}

/************************************************************************* 
 * 
 * SeparateRGBfromDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB        - handle to global memory with a DIB spec 
 *                    in it followed by the DIB bits 
 * 
 * int  nIndex      - index to specify the color to Separate from DIB
 *
 * 
 * Return Value: 
 * 
 * HDIB		        - the handle of result DIB
 *
 * 
 * Description: 
 *   Separate DIB to R, G, B
 * 
 ************************************************************************/ 
HDIB SeparateRGBfromDIB(HDIB hDib, int nIndex)
{
	if (hDib == NULL)
		return NULL;
	HDIB hNewDIB = CopyHandle(hDib);
	if (hNewDIB == NULL)
		return NULL;
	BITMAPINFO *bmi = (BITMAPINFO *)GlobalLock(hNewDIB);
	if (! bmi)
		return NULL;

	WaitCursorBegin();

	// get color number
	WORD wNumColors = DIBNumColors((LPBYTE)bmi);

	if (wNumColors)		// There is palette
	{
		for (WORD i=0; i<wNumColors; i++)
		{
			switch (nIndex)
			{
			case SEPARATE_RED:
				 bmi->bmiColors[i].rgbGreen = 0;
				 bmi->bmiColors[i].rgbBlue = 0;
				 break;
			case SEPARATE_GREEN:
				 bmi->bmiColors[i].rgbRed = 0;
				 bmi->bmiColors[i].rgbBlue = 0;
				 break;
			case SEPARATE_BLUE:
				 bmi->bmiColors[i].rgbRed = 0;
				 bmi->bmiColors[i].rgbGreen = 0;
				 break;
			}
		}
	}
	else		// No palette
	{
		// bits position
		LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)bmi;
		LPBYTE lpBits = (LPBYTE)lpbi + lpbi->biSize;
		int nDelta = WIDTHBYTES(lpbi->biBitCount*lpbi->biWidth) - lpbi->biWidth*lpbi->biBitCount/8;
		for (int ny=0; ny<lpbi->biHeight; ny++)
		{
			for (int nx=0; nx<lpbi->biWidth; nx++)
			{
				switch (nIndex)
				{
				case SEPARATE_RED:
					*lpBits++ = 0;
					*lpBits++ = 0;
					lpBits++;
					 break;
				case SEPARATE_GREEN:
					*lpBits++ = 0;
					lpBits++;
					*lpBits++ = 0;
					 break;
				case SEPARATE_BLUE:
					lpBits++;
					*lpBits++ = 0;
					*lpBits++ = 0;
					 break;
				}
			}
			lpBits += nDelta; 
		}
	}

	GlobalUnlock(hNewDIB);
	WaitCursorEnd();

	return hNewDIB;
}

/************************************************************************* 
 * 
 * FilteRGBfromDIB() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB        - handle to global memory with a DIB spec 
 *                    in it followed by the DIB bits 
 * 
 * int  nIndex      - index to specify the color to filte from DIB
 *
 * 
 * Return Value: 
 * 
 * HDIB		        - the handle of result DIB
 *
 * 
 * Description: 
 *   Filte DIB to R, G, B
 * 
 ************************************************************************/ 
HDIB FilteRGBfromDIB(HDIB hDib, int nIndex)
{
	if (hDib == NULL)
		return NULL;
	HDIB hNewDIB = CopyHandle(hDib);
	if (hNewDIB == NULL)
		return NULL;
	BITMAPINFO *bmi = (BITMAPINFO *)GlobalLock(hNewDIB);
	if (! bmi)
		return NULL;

	WaitCursorBegin();

	// get color number
	WORD wNumColors = DIBNumColors((LPBYTE)bmi);

	if (wNumColors)		// There is palette
	{
		for (WORD i=0; i<wNumColors; i++)
		{
			switch (nIndex)
			{
			case FILTE_RED:
				 bmi->bmiColors[i].rgbRed = 0;
				 break;
			case FILTE_GREEN:
				 bmi->bmiColors[i].rgbGreen = 0;
				 break;
			case FILTE_BLUE:
				 bmi->bmiColors[i].rgbBlue = 0;
				 break;
			}
		}
	}
	else		// No palette
	{
		// bits position
		LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)bmi;
		LPBYTE lpBits = (LPBYTE)lpbi + lpbi->biSize;
		int nDelta = WIDTHBYTES(lpbi->biBitCount*lpbi->biWidth) - lpbi->biWidth*lpbi->biBitCount/8;
		for (int ny=0; ny<lpbi->biHeight; ny++)
		{
			for (int nx=0; nx<lpbi->biWidth; nx++)
			{
				switch (nIndex)
				{
				case FILTE_RED:
					lpBits++;
					lpBits++;
					*lpBits++ = 0;
					 break;
				case FILTE_GREEN:
					lpBits++;
					*lpBits++ = 0;
					lpBits++;
					 break;
				case FILTE_BLUE:
					*lpBits++ = 0;
					lpBits++;
					lpBits++;
					 break;
				}
			}
			lpBits += nDelta; 
		}
	}

	GlobalUnlock(hNewDIB);
	WaitCursorEnd();

	return hNewDIB;
}

/************************************************************************* 
 * 
 * AdjustDIBBrightness() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB        - handle to global memory with a DIB spec 
 *                    in it followed by the DIB bits 
 * 
 * int v		    - changed value to color value
 *
 * 
 * Return Value: 
 * 
 * BOOL				- TRUE is success, else FALSE
 * 
 * Description: 
 *   Adjust DIB brightness
 * 
 ************************************************************************/ 
BOOL AdjustDIBBrightness(HDIB hDib, int v)
{
	if (hDib == NULL)
		return FALSE;
	BITMAPINFO *bmi = (BITMAPINFO *)GlobalLock(hDib);
	if (! bmi)
		return FALSE;

	WaitCursorBegin();

	// get color number
	WORD wNumColors = DIBNumColors((LPBYTE)bmi);

	if (wNumColors)		// There is palette
	{
		for (WORD i=0; i<wNumColors; i++)
		{
			ChangeBrightness(v, 
							 &(bmi->bmiColors[i].rgbRed),
							 &(bmi->bmiColors[i].rgbGreen),
							 &(bmi->bmiColors[i].rgbBlue));
		}
	}
	else		// No palette
	{
		// bits position
		LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)bmi;
		LPBYTE lpBits = (LPBYTE)lpbi + lpbi->biSize;
		int nDelta = WIDTHBYTES(lpbi->biBitCount*lpbi->biWidth) - lpbi->biWidth*lpbi->biBitCount/8;
		BYTE r, g, b;
		for (int ny=0; ny<lpbi->biHeight; ny++)
		{
			for (int nx=0; nx<lpbi->biWidth; nx++)
			{
				b = (BYTE)*(lpBits);
				g = (BYTE)*(lpBits+1);
				r = (BYTE)*(lpBits+2);
				ChangeBrightness(v, &r, &g, &b);
				*lpBits++ = b;
				*lpBits++ = g;
				*lpBits++ = r;
			}
			lpBits += nDelta; 
		}
	}

	GlobalUnlock(hDib);
	WaitCursorEnd();
	return TRUE;
}

/************************************************************************* 
 * 
 * AdjustDIBContrast() 
 * 
 * Parameters: 
 * 
 * HDIB hDIB        - handle to global memory with a DIB spec 
 *                    in it followed by the DIB bits 
 * 
 * int v		    - changed value to color value
 *
 * 
 * Return Value: 
 * 
 * BOOL				- TRUE is success, else FALSE
 * 
 * Description: 
 *   Adjust DIB contrast
 * 
 ************************************************************************/ 
BOOL AdjustDIBContrast(HDIB hDib, int v)
{
	if (hDib == NULL)
		return FALSE;
	BITMAPINFO *bmi = (BITMAPINFO *)GlobalLock(hDib);
	if (! bmi)
		return FALSE;

	WaitCursorBegin();

	// get color number
	WORD wNumColors = DIBNumColors((LPBYTE)bmi);

	if (wNumColors)		// There is palette
	{
		for (WORD i=0; i<wNumColors; i++)
		{
			ChangeContrast(v, 
						   &(bmi->bmiColors[i].rgbRed),
						   &(bmi->bmiColors[i].rgbGreen),
						   &(bmi->bmiColors[i].rgbBlue));
		}
	}
	else		// No palette
	{
		// bits position
		LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)bmi;
		LPBYTE lpBits = (LPBYTE)lpbi + lpbi->biSize;
		int nDelta = WIDTHBYTES(lpbi->biBitCount*lpbi->biWidth) - lpbi->biWidth*lpbi->biBitCount/8;
		BYTE r, g, b;
		for (int ny=0; ny<lpbi->biHeight; ny++)
		{
			for (int nx=0; nx<lpbi->biWidth; nx++)
			{
				b = (BYTE)*(lpBits);
				g = (BYTE)*(lpBits+1);
				r = (BYTE)*(lpBits+2);
				ChangeContrast(v, &r, &g, &b);
				*lpBits++ = b;
				*lpBits++ = g;
				*lpBits++ = r;
			}
			lpBits += nDelta; 
		}
	}

	GlobalUnlock(hDib);
	WaitCursorEnd();
	return TRUE;
}

/************************************************************************* 
 * 
 * HSItoRGB() 
 * 
 * Parameters: 
 * 
 * h, s, i        - input h, s, i value
 * 
 * r, g, b        - output r, g, b value
 *
 * 
 * Return Value:  none
 * 
 * 
 * Description: 
 *   Change HSI color to RGB color
 * 
 ************************************************************************/ 
void HSItoRGB(double h, double s, double i, BYTE *r, BYTE *g, BYTE *b)
{
	double V1 = s * cos(h);
	double V2 = s * sin(h);
	int iR = (int)( 0.816496*V2 + 0.57735*i);
	int iG = (int)( 0.7071  *V1 - 0.40824*V2 + 0.57735*i);
	int iB = (int)(-0.7071  *V1 - 0.40824*V2 + 0.57735*i);
	*r = BOUND(iR, 0, 255);
	*g = BOUND(iG, 0, 255);
	*b = BOUND(iB, 0, 255);
}

/************************************************************************* 
 * 
 * RGBtoHSI() 
 * 
 * Parameters: 
 * 
 * r, g, b        - input r, g, b value
 * 
 * h, s, i        - output h, s, i value
 *
 * 
 * Return Value:  none
 * 
 * 
 * Description: 
 *   Change RGB color to HSI color
 * 
 ************************************************************************/ 
void RGBtoHSI(BYTE r, BYTE g, BYTE b, double *h, double *s, double *i)
{
	double V1 = 0.7071*((double)g-(double)b);
	double V2 = 0.816496*(double)r-0.40824*((double)g+(double)b);

	*h = atan2(V1, V2);
	*s = sqrt(V1*V1+V2*V2);
	*i = 0.57735*((double)r+(double)g+(double)b);
}

// local function used in HLStoRGB
double Value(double n1, double n2, double hue)
{
	if (hue > 360.0)
		hue -= 360.0;
	else if (hue < 0.0)
		hue += 360.0;
	
	if (hue < 60.0)
		return (n1+(n2-n1)*hue/60.0);
	else if (hue < 180.0)
		return n2;
	else if (hue < 240.0)
		return (n1+(n2-n1)*(240.0-hue)/60.0);
	else
		return n1;
}

/************************************************************************* 
 * 
 * HLStoRGB() 
 * 
 * Parameters: 
 * 
 * h, l, s        - input h, l, s value
 * 
 * r, g, b        - output r, g, b value
 *
 * 
 * Return Value:  none
 * 
 * 
 * Description: 
 *   Change HLS color to RGB color
 * 
 ************************************************************************/ 
void HLStoRGB(double h, double l, double s, BYTE *r, BYTE *g, BYTE *b)
{
	double m1, m2;
	double R, G, B;

	if (l <= 0.5)
		m2 = l*(1.0+s);
	else
		m2 = l+s-l*s;

	m1 = 2.0*l-m2;
	if (s == 0)
	{
		if (h == UNDEFINED)
			R = G = B = l;
		else	// error!!
			R = G = B = 0.0;
	}
	else
	{
		R = Value(m1, m2, h+120.0);
		G = Value(m1, m2, h);
		B = Value(m1, m2, h-120.0);
	}
	int iR = (int)(R*255.0);
	int iG = (int)(G*255.0);
	int iB = (int)(B*255.0);
	*r = (BYTE)BOUND(iR, 0, 255);
	*g = (BYTE)BOUND(iG, 0, 255);
	*b = (BYTE)BOUND(iB, 0, 255);
}

/************************************************************************* 
 * 
 * RGBtoHLS() 
 * 
 * Parameters: 
 * 
 * r, g, b        - input r, g, b value
 * 
 * h, l, s        - output h, l, s value
 *
 * 
 * Return Value:  none
 * 
 * 
 * Description: 
 *   Change RGB color to HLS color
 * 
 ************************************************************************/ 
void RGBtoHLS(BYTE r, BYTE g, BYTE b, double *h, double *l, double *s)
{
	double mx, mn, delta;
	double R, G, B;

	R = (double)r/255.0;
	G = (double)g/255.0;
	B = (double)b/255.0;
	mx = max(R, max(G, B));
	mn = min(R, min(G, B));
	*l = (mx+mn)/2.0;
	if (mx == mn)
	{
		*s = 0.0;
		*h = UNDEFINED;		// undefined!
	}
	else
	{
		delta = mx-mn;
		if (*l < 0.5)
			*s = delta/(mx+mn);
		else
			*s = delta/(2.0-mx-mn);
		if (R == mx)
			*h = (G-B)/delta;
		else if (G == mx)
			*h = 2.0+(B-R)/delta;
		else if (B == mx)
			*h = 4.0+(R-G)/delta;
		*h *= 60.0;
		if (*h < 0.0)
			*h += 360.0;
		else if (*h > 360.0)
			*h -= 360.0;
	}
}

/************************************************************************* 
 * 
 * CMYKtoRGB() 
 * 
 * Parameters: 
 * 
 * c, m, y, k     - input c, m, y, k value
 * 
 * r, g, b        - output r, g, b value
 *
 * 
 * Return Value:  none
 * 
 * 
 * Description: 
 *   Change CMYK color to RGB color
 * 
 ************************************************************************/ 
void CMYKtoRGB(BYTE c, BYTE m, BYTE y, BYTE k, BYTE *r, BYTE *g, BYTE *b)
{
	c += k;
	m += k;
	y += k;

	*r = 255 - c;
	*g = 255 - m;
	*b = 255 - y;
}

/************************************************************************* 
 * 
 * RGBtoCMYK() 
 * 
 * Parameters: 
 * 
 * r, g, b        - input r, g, b value
 * 
 * c, m, y, k     - output c, m, y, k value
 *
 * 
 * Return Value:  none
 * 
 * 
 * Description: 
 *   Change RGB color to CMYK color
 * 
 ************************************************************************/ 
void RGBtoCMYK(BYTE r, BYTE g, BYTE b, BYTE *c, BYTE *m, BYTE *y, BYTE *k)
{
	*c = 255 - r;
	*m = 255 - g;
	*y = 255 - b;

	*k = min(*c, min(*m, *y));
	*c -= *k;
	*m -= *k;
	*y -= *k;
}

// IncreaseContrast 
void IncreaseContrast(BYTE *pByte, const int Low, const int High, const float Grad)
{ 
    if (*pByte <= Low) 
        *pByte = 0; 
    else if ((Low < *pByte) && (*pByte <  High))
		*pByte = (BYTE)( (*pByte - Low) / Grad); 
    else // pElem->rgbGreen >= High 
        *pByte = 255; 
}

 // DecreaseContrast 
void DecreaseContrast(BYTE *pByte, const int Level, const float Grad)
{ 
    ASSERT(pByte); 
    ASSERT(Grad != 0.0); 
    *pByte = (BYTE) ( ((int) (*pByte / Grad)) - Level); 
}

/************************************************************************* 
 * 
 * ChangeContrast() 
 * 
 * Parameters: 
 * 
 * int nDelta     - delta value to R, G, B
 *
 * r, g, b        - r, g, b value to change
 * 
 * 
 * Return Value:  none
 * 
 * 
 * Description: 
 *   Change contrast
 * 
 ************************************************************************/ 
void ChangeContrast(int nDelta, BYTE *r, BYTE *g, BYTE *b)
{
	if (nDelta >= 0)
	{
        int Low  = 0 + nDelta; 
        int High = 255 - nDelta; 
		float Grad= ((float)(High - Low)) / 255; 
	    IncreaseContrast(r, Low, High, Grad); 
		IncreaseContrast(g, Low, High, Grad); 
		IncreaseContrast(b, Low, High, Grad); 
	}
	else
	{
        float Grad = 255 / (255 + (float)nDelta + (float)nDelta); 
	    DecreaseContrast(r, nDelta, Grad); 
		DecreaseContrast(g, nDelta, Grad); 
		DecreaseContrast(b, nDelta, Grad); 
	}
}

/************************************************************************* 
 * 
 * ChangeBrightness() 
 * 
 * Parameters: 
 * 
 * int nDelta     - delta value to R, G, B
 *
 * r, g, b        - r, g, b value to change
 * 
 * 
 * Return Value:  none
 * 
 * 
 * Description: 
 *   Change brightness
 * 
 ************************************************************************/ 
void ChangeBrightness(int nDelta, BYTE *r, BYTE *g, BYTE *b)
{
	int R = *r + nDelta;
	int G = *g + nDelta;
	int B = *b + nDelta;

	*r = (BYTE)BOUND(R, 0, 255);
	*g = (BYTE)BOUND(G, 0, 255);
	*b = (BYTE)BOUND(B, 0, 255);
}

///////////////////// local used only !!! /////////////////////////////

// macros used to dither
#define ALIGN_DWORD(x) (((x)+3)/4 * 4)  // Double word alignment macro

// Macros used to facilitate RGB pixel access for dithering
#define GetRGBPixel(Addr, Red, Green, Blue)			\
				  Blue  = (int) *(Addr);			\
				  Green = (int) *(Addr+1);			\
				  Red   = (int) *(Addr+2);


#define PutRGBPixel(Addr, Red, Green, Blue)			\
				  Red = (Red<0)   ? 0 : Red;        \
				  Red = (Red>255) ? 255 : Red;      \
				  Green = (Green<0)   ? 0 : Green;  \
				  Green = (Green>255) ? 255 : Green;\
				  Blue = (Blue<0)   ? 0 : Blue;     \
				  Blue = (Blue>255) ? 255 : Blue;   \
				  *(Addr)   = (BYTE)Blue;           \
				  *(Addr+1) = (BYTE)Green;          \
				  *(Addr+2) = (BYTE)Red;

// This is the palette with which all true color images will be
// displayed when hardware support is unavailable.
typedef struct  
{
  BYTE Red;
  BYTE Green;
  BYTE Blue;
} RGBCOLOR;

RGBCOLOR DitherPalette[256] = 
{
     {0,   0,   0},   {0,   0,  85},   {0,   0, 170},  {0,   0, 255},
     {0,  36,   0},   {0,  36,  85},   {0,  36, 170},  {0,  36, 255},
     {0,  73,   0},   {0,  73,  85},   {0,  73, 170},  {0,  73, 255},
     {0, 109,   0},   {0, 109,  85},   {0, 109, 170},  {0, 109, 255},
     {0, 146,   0},   {0, 146,  85},   {0, 146, 170},  {0, 146, 255},
     {0, 182,   0},   {0, 182,  85},   {0, 182, 170},  {0, 182, 255},
     {0, 219,   0},   {0, 219,  85},   {0, 219, 170},  {0, 219, 255},
     {0, 255,   0},   {0, 255,  85},   {0, 255, 170},  {0, 255, 255},

    {36,   0,   0},  {36,   0,  85},  {36,   0, 170}, {36,   0, 255},
    {36,  36,   0},  {36,  36,  85},  {36,  36, 170}, {36,  36, 255},
    {36,  73,   0},  {36,  73,  85},  {36,  73, 170}, {36,  73, 255},
    {36, 109,   0},  {36, 109,  85},  {36, 109, 170}, {36, 109, 255},
    {36, 146,   0},  {36, 146,  85},  {36, 146, 170}, {36, 146, 255},
    {36, 182,   0},  {36, 182,  85},  {36, 182, 170}, {36, 182, 255},
    {36, 219,   0},  {36, 219,  85},  {36, 219, 170}, {36, 219, 255},
    {36, 255,   0},  {36, 255,  85},  {36, 255, 170}, {36, 255, 255},

    {73,   0,   0},  {73,   0,  85},  {73,   0, 170}, {73,   0, 255},
    {73,  36,   0},  {73,  36,  85},  {73,  36, 170}, {73,  36, 255},
    {73,  73,   0},  {73,  73,  85},  {73,  73, 170}, {73,  73, 255},
    {73, 109,   0},  {73, 109,  85},  {73, 109, 170}, {73, 109, 255},
    {73, 146,   0},  {73, 146,  85},  {73, 146, 170}, {73, 146, 255},
    {73, 182,   0},  {73, 182,  85},  {73, 182, 170}, {73, 182, 255},
    {73, 219,   0},  {73, 219,  85},  {73, 219, 170}, {73, 219, 255},
    {73, 255,   0},  {73, 255,  85},  {73, 255, 170}, {73, 255, 255},

  {109,   0,   0}, {109,   0,  85}, {109,   0, 170}, {109,   0, 255},
  {109,  36,   0}, {109,  36,  85}, {109,  36, 170}, {109,  36, 255},
  {109,  73,   0}, {109,  73,  85}, {109,  73, 170}, {109,  73, 255},
  {109, 109,   0}, {109, 109,  85}, {109, 109, 170}, {109, 109, 255},
  {109, 146,   0}, {109, 146,  85}, {109, 146, 170}, {109, 146, 255},
  {109, 182,   0}, {109, 182,  85}, {109, 182, 170}, {109, 182, 255},
  {109, 219,   0}, {109, 219,  85}, {109, 219, 170}, {109, 219, 255},
  {109, 255,   0}, {109, 255,  85}, {109, 255, 170}, {109, 255, 255},

  {146,   0,   0}, {146,   0,  85}, {146,   0, 170}, {146,   0, 255},
  {146,  36,   0}, {146,  36,  85}, {146,  36, 170}, {146,  36, 255},
  {146,  73,   0}, {146,  73,  85}, {146,  73, 170}, {146,  73, 255},
  {146, 109,   0}, {146, 109,  85}, {146, 109, 170}, {146, 109, 255},
  {146, 146,   0}, {146, 146,  85}, {146, 146, 170}, {146, 146, 255},
  {146, 182,   0}, {146, 182,  85}, {146, 182, 170}, {146, 182, 255},
  {146, 219,   0}, {146, 219,  85}, {146, 219, 170}, {146, 219, 255},
  {146, 255,   0}, {146, 255,  85}, {146, 255, 170}, {146, 255, 255},

  {182,   0,   0}, {182,   0,  85}, {182,   0, 170}, {182,   0, 255},
  {182,  36,   0}, {182,  36,  85}, {182,  36, 170}, {182,  36, 255},
  {182,  73,   0}, {182,  73,  85}, {182,  73, 170}, {182,  73, 255},
  {182, 109,   0}, {182, 109,  85}, {182, 109, 170}, {182, 109, 255},
  {182, 146,   0}, {182, 146,  85}, {182, 146, 170}, {182, 146, 255},
  {182, 182,   0}, {182, 182,  85}, {182, 182, 170}, {182, 182, 255},
  {182, 219,   0}, {182, 219,  85}, {182, 219, 170}, {182, 219, 255},
  {182, 255,   0}, {182, 255,  85}, {182, 255, 170}, {182, 255, 255},

  {219,   0,   0}, {219,   0,  85}, {219,   0, 170}, {219,   0, 255},
  {219,  36,   0}, {219,  36,  85}, {219,  36, 170}, {219,  36, 255},
  {219,  73,   0}, {219,  73,  85}, {219,  73, 170}, {219,  73, 255},
  {219, 109,   0}, {219, 109,  85}, {219, 109, 170}, {219, 109, 255},
  {219, 146,   0}, {219, 146,  85}, {219, 146, 170}, {219, 146, 255},
  {219, 182,   0}, {219, 182,  85}, {219, 182, 170}, {219, 182, 255},
  {219, 219,   0}, {219, 219,  85}, {219, 219, 170}, {219, 219, 255},
  {219, 255,   0}, {219, 255,  85}, {219, 255, 170}, {219, 255, 255},

  {255,   0,   0}, {255,   0,  85}, {255,   0, 170}, {255,   0, 255},
  {255,  36,   0}, {255,  36,  85}, {255,  36, 170}, {255,  36, 255},
  {255,  73,   0}, {255,  73,  85}, {255,  73, 170}, {255,  73, 255},
  {255, 109,   0}, {255, 109,  85}, {255, 109, 170}, {255, 109, 255},
  {255, 146,   0}, {255, 146,  85}, {255, 146, 170}, {255, 146, 255},
  {255, 182,   0}, {255, 182,  85}, {255, 182, 170}, {255, 182, 255},
  {255, 219,   0}, {255, 219,  85}, {255, 219, 170}, {255, 219, 255},
  {255, 255,   0}, {255, 255,  85}, {255, 255, 170}, {255, 255, 255}
};

// Define temporary vars as global, try to speedup...
int  TRed, TGreen, TBlue;
int  RedError, GreenError, BlueError;
BYTE PaletteIndex;
int  BlueOffset;
LPBYTE PixelAddr;
LPBYTE RPixelAddr;
LPBYTE DPixelAddr;

// Dither a single row of image data
void DitherLine(WORD Width, LPBYTE pLine1, LPBYTE pLine2) 
{
	for (register WORD Pixel=0; Pixel < Width; Pixel++) 
	{
		// Get the pixel of interest for the dither
		PixelAddr = pLine1 + (Pixel * 3);
		GetRGBPixel(PixelAddr, TRed, TGreen, TBlue);
		// Determine which blue entry from palette to use
		BlueOffset = (TBlue/85);
		if ((TBlue - (BlueOffset * 85)) > 43)
		BlueOffset += 1;
		// Calculate palette entry address from color
		// PaletteIndex = ((TRed/32)*32) + ((TGreen/32)*4) + BlueOffset;
		// For speed we will calculate the color index as follows
		PaletteIndex = (TRed & 0xE0) + ((TGreen >> 5) << 2) + BlueOffset;
		// Store the palette back into the same buffer
		*(pLine1 + Pixel) = PaletteIndex;
		/*
		Pixel is set, now distribute the error to adjacent pixels
		using a modified version of the Floyd-Steinberg algorithm.
		In this implementation the error is distributed as follows
						   Pixel
									  o 1/2->o
								 1/4 |
							 o   
		The algorithm is modified to increase performance.
		*/
		// Calculate errors between the desired color and color used
		// for this pixel. Actual error / 4. Use  pointers for speed.

		BYTE  *pColor = (BYTE  *) &DitherPalette[PaletteIndex];  // Ptr to color
		RedError   = (TRed   - *(pColor))   >> 2;
		GreenError = (TGreen - *(pColor+1)) >> 2;
		BlueError  = (TBlue  - *(pColor+2)) >> 2;

		// Do the pixel directly below target pixel
		DPixelAddr  = pLine2 + (Pixel * 3);
		GetRGBPixel(DPixelAddr, TRed, TGreen, TBlue);
		TRed   += RedError;                // 1/4 error
		TGreen += GreenError;
		TBlue  += BlueError;
		PutRGBPixel(DPixelAddr, TRed, TGreen, TBlue);
		// Do the pixel directly to the right
		if (Pixel != Width - 1)  
		{
			RPixelAddr  = PixelAddr + 3;
			GetRGBPixel(RPixelAddr, TRed, TGreen, TBlue);
			TRed   += RedError   + RedError;   // 1/2 error
			TGreen += GreenError + GreenError;
			TBlue  += BlueError  + BlueError;
			PutRGBPixel(RPixelAddr, TRed, TGreen, TBlue);
		}
	}
}

// This function dithers the image contained in this object. It returns
// a handle to a global memory block containing the dithered data.
HGLOBAL DitherImage(LPBYTE lpImageData, WORD Width, WORD Height) 
{
	// Calculate width in bytes of a row of RGB data
	WORD BytesPerLine = ALIGN_DWORD(Width * 3);   // Source 24 BPP image
	// Calculate width in bytes of a row of palettized data
	WORD BytesPerLineDest = ALIGN_DWORD(Width);   // Dest 8 BPP image

	// Allocate two lines of RGB buffer for dithering
	HLOCAL hLine1Buffer = LocalAlloc(LMEM_MOVEABLE, BytesPerLine);
	if (!hLine1Buffer) 
		return NULL;

	HLOCAL hLine2Buffer = LocalAlloc(LMEM_MOVEABLE, BytesPerLine);
	if (!hLine2Buffer) 
	{
		LocalFree(hLine1Buffer);
		return NULL;
	}

	// Allocate the destination dither buffer
	HGLOBAL hMem = GlobalAlloc(GHND, (DWORD) BytesPerLineDest * Height);
	if (!hMem) 
	{
		LocalFree(hLine1Buffer);
		LocalFree(hLine2Buffer);
		return NULL;
	}

	// Now lock the pointers for access
	LPBYTE Line1Buffer = (LPBYTE) LocalLock(hLine1Buffer);
	LPBYTE Line2Buffer = (LPBYTE) LocalLock(hLine2Buffer);
	LPBYTE lpDitheredRasterData = (LPBYTE) GlobalLock(hMem);

	// Move the first two lines of the source image to dither buffers
	memcpy(Line1Buffer, lpImageData, BytesPerLine);
	memcpy(Line2Buffer, lpImageData + BytesPerLine, BytesPerLine);

	for (register WORD Row = 2; Row < Height; Row++) 
	{
		DitherLine(Width, Line1Buffer, Line2Buffer);
		// Copy the dithered data in Line1Buffer to destination
		memcpy(lpDitheredRasterData  + ((Row-2) * (DWORD) BytesPerLineDest),
		Line1Buffer, BytesPerLineDest);
		// Copy Line2Buffer to Line1Buffer so it can be dithered
		memcpy(Line1Buffer, Line2Buffer, BytesPerLine);

		// Move new data to Line2Buffer
		memcpy(Line2Buffer, lpImageData  + (Row * (DWORD) BytesPerLine),
		BytesPerLine);
	}
	// Must complete the two rows in the line buffers
	DitherLine(Width, Line1Buffer, Line2Buffer);
	// Copy the dithered data in Line1Buffer to destination
	memcpy(lpDitheredRasterData  + ((Height-2) * (DWORD) BytesPerLineDest),
	Line1Buffer, BytesPerLineDest);
	memcpy(Line1Buffer, Line2Buffer, BytesPerLine);

	DitherLine(Width, Line1Buffer, Line2Buffer);
	// Copy the dithered data in Line1Buffer to destination
	memcpy(lpDitheredRasterData  + ((Height-1) * (DWORD) BytesPerLineDest),
	Line1Buffer, BytesPerLineDest);

	// Free the local line buffers
	LocalUnlock(hLine1Buffer);
	LocalFree(hLine1Buffer);
	LocalUnlock(hLine2Buffer);
	LocalFree(hLine2Buffer);

	// Signal all is well
	GlobalUnlock(hMem);             // Unlock the dithered raster data
	return hMem;                    // Return the handle of the data
}

///////////////////// local used only !!! /////////////////////////////

// create a dithered 8bpp DIB for 24bpp DIB in not true color device
HDIB CreateDither8BppDIB(HDIB hDIB)
{
	WaitCursorBegin();

	if (DIBBitCount(hDIB) <= 8)
	{
		WaitCursorEnd();
		return CopyHandle(hDIB);
	}

    // dithering DIB
	LPBYTE lpDIB = (LPBYTE)GlobalLock(hDIB);
	LPBYTE lpBits = FindDIBBits(lpDIB);
	WORD wWidth = (WORD)DIBWidth(lpDIB);
	WORD wHeight = (WORD)DIBHeight(lpDIB);
    HGLOBAL hDitheredBits = DitherImage(lpBits, wWidth, wHeight);
	GlobalUnlock(hDIB);
	if (! hDitheredBits)
	{
		WaitCursorEnd();
		return NULL;
	}

	// create a new 8bpp DIB
	HDIB hNewDib = ChangeDIBFormat(hDIB, 8, BI_RGB);;
	LPBITMAPINFO lpBmi = (LPBITMAPINFO)GlobalLock(hNewDib);
	lpBmi->bmiHeader.biClrUsed = 256;
    
	// set the entries of dither palette to DIB color table
    for (int Index=0; Index < 256; Index++)  
	{
		lpBmi->bmiColors[Index].rgbRed   = DitherPalette[Index].Red;
		lpBmi->bmiColors[Index].rgbGreen = DitherPalette[Index].Green;
		lpBmi->bmiColors[Index].rgbBlue  = DitherPalette[Index].Blue;
		lpBmi->bmiColors[Index].rgbReserved = 0;
    }

	// copy dithered image bits to hNewDib
	DWORD dwSize = GlobalSize(hDitheredBits);
	LPBYTE lpDitheredBits = (LPBYTE)GlobalLock(hDitheredBits);
	LPBYTE lpNewBits = FindDIBBits((LPBYTE)lpBmi);
	memcpy(lpNewBits, lpDitheredBits, dwSize);

	// cleanup
	GlobalUnlock(hDitheredBits);
	GlobalFree(hDitheredBits);
	GlobalUnlock(hNewDib);

	// map dithered DIB to dither palette
	HPALETTE hPal = CreateDitherPalette();
	MapDIBColorsToPalette(hNewDib, hPal);
	DeleteObject(hPal);

	WaitCursorEnd();
	return hNewDib;
}

// create dither palette for dithering
HPALETTE CreateDitherPalette()
{ 
    LPLOGPALETTE        lpPal;          // pointer to a logical palette 
    HANDLE              hLogPal;        // handle to a logical palette 
    HPALETTE            hPal = NULL;    // handle to a palette 
    HPALETTE            hPalette = NULL;    // handle to a palette 
    int                 i;  // loop index, number of colors in color table 
 
    // allocate memory block for logical palette 

    hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * 256);

    // if not enough memory, clean up and return NULL 

    if (!hLogPal) 
        return NULL; 

    // lock memory block and get pointer to it 

    lpPal = (LPLOGPALETTE)GlobalLock(hLogPal); 

    // set version and number of palette entries 

    lpPal->palVersion = PALVERSION; 
    lpPal->palNumEntries = 256;

	// set dithered palette value  
    for (i = 0; i < 256; i++) 
    { 
        lpPal->palPalEntry[i].peRed   = DitherPalette[i].Red; 
        lpPal->palPalEntry[i].peGreen = DitherPalette[i].Green; 
        lpPal->palPalEntry[i].peBlue  = DitherPalette[i].Blue; 
        lpPal->palPalEntry[i].peFlags = 0; 
    } 

    // create the palette and get handle to it 

    hPal = CreatePalette(lpPal); 

    // if error getting handle to palette, clean up and return NULL 

    if (!hPal) 
    { 
        GlobalUnlock(hLogPal); 
        GlobalFree(hLogPal); 
        return NULL; 
    } 

	// clean up  
 
    GlobalUnlock(hLogPal); 
    GlobalFree(hLogPal); 

	// change palette to identify palette
	
	hPalette = CreateIdentifyPalette(hPal);

	// return idenify palette is it's ok
	// else return the original one

	if (hPalette)
	{
		DeleteObject(hPal);
 	    return hPalette; 
	}
	else
 	    return hPal; 
}